# [IMPL-RPT-040] CC-zip(1) 命令对齐账本(claude-code-main (1).zip)

> 对照 `/home/kodehu03/Downloads/claude-code-main (1).zip`(129 个 CC 命令)与 khy(195 router 命令名)
> 的逐项 diff。每条均经 node 提取 + Explore 读 CC 源理解**背后逻辑**(非显示)后分类。
> 三类:**EQUIVALENT**(khy 已有同义逻辑,补 `/` 别名即可)/**NEW-LOCAL**(khy 无、可诚实本地实现)/
> **INFRA-NA**(云/CI/命名管道 IPC/远端守护/订阅计费,本地 CLI 无法诚实实现,绝不造空壳)。
>
> 审计法:`node` 直读(grep 经 rtk 污染不可信)。CC 命令名经 `src/commands/<name>` 提取,khy 经
> `getRouterCommandNames()`。

## A. NEW-LOCAL —— 真缺口,逐个实现(接线五件套)

| CC 命令 | 背后逻辑 | khy 现状 | 实现要点 |
|---|---|---|---|
| `debug-tool-call` | 解析 transcript JSONL,按 id 配对 tool_use↔tool_result,截 200 字,显示最近 N(默认5) | 无 | 纯叶子解析 + 薄壳读 transcript。零模型零网络,最干净 |
| `recap` | 「离开期间」一句话回顾;CC forked 单轮模型查询 | 有 `sessionRecapService`(consolidate 在用)但**无命令** | 薄壳命令复用既有 recap 服务;无模型→确定性 summary 诚实降级 |
| `copy` | 复制最近(或第N条)助手回复 / 其中代码块到剪贴板(OSC52 + tmp 文件兜底) | 无 | 叶子选回复/抽代码块 + 薄壳剪贴板写 |
| `rename` | 重命名当前会话;无参时模型生成 kebab 名 | 无(有 session 管理无 rename) | 薄壳写 session 标题;无模型→提示需显式名 |
| `tag` | 给会话打可搜索标签(同标签再打=确认移除) | 无 | 薄壳 session 元数据写 |
| `color` | 设会话提示条颜色(per-session) | 有 `theme`/`skin`(全局)非 per-session | 低优先;可并入 skin |
| `break-cache` | marker 文件强制下次/全部 API 调用 miss 前缀缓存 | 无 | 低优先;niche 调试 |
| `heapdump` | V8 堆快照落盘 | 无 | 低优先;niche 调试 |

## B. EQUIVALENT —— khy 已有同义逻辑,补 CC 名作别名即「菜单补齐」

| CC 命令 | khy 等价 |
|---|---|
| `mode` | `persona` / `role`(行为预设) |
| `security-review` | `security`(scan/audit/permissions 子命令) |
| `force-snip` | `snip`(手动裁剪近期消息) |
| `skill-learning` | `skill learn` / `skill learned` / `learn` / `evolve` / `train` |
| `skill-search` | `skill search` 子命令 |
| `skills` | `skill list` |
| `local-vault` | `vault`(list/set/get/show/rm 密钥库) |
| `local-memory` | `memory` / `knowledge` / `remember` |
| `provider` | `gateway config` / `model`(选 AI 服务商) / NL provider 配置 |
| `reload-plugins` | `plugin reload` 子命令 |
| `insights` | `insights`(已存在,完全等价) |
| `commit-push-pr` | `pr create`(git/gh 编排) |
| `web-tools` | NL provider 配置 + `web_search` / `search` |
| `poor` | `effort`(low/medium/high token 档) |
| `tui` | khy 默认全 TUI(KHY_FULL_TUI),无需独立开关 |
| `workflows` | `workflow`(canonical 工作流 CLI) |
| `sandbox` | `sandbox-toggle` |

## C. INFRA-NA —— 结构性缺失,诚实不实现(不造空壳)

| 类别 | CC 命令 |
|---|---|
| Anthropic 云 REST(订阅/计费/账号) | `memory-stores` `skill-store` `artifacts` `privacy-settings` `extra-usage` `rate-limit-options` `passes` `feedback` `agents-platform` `schedule`(triggers 云 cron) |
| 命名管道 / UDS / LAN 多进程 IPC | `send` `attach` `detach` `peers` `pipes` `pipe-status` |
| 云桥 / 远端守护 / 远端环境 | `remote-control` `remote-control-server` `remote-env` `teleport` `web-setup` `bridge-kick` |
| GitHub/Slack 云集成 | `install-github-app` `install-slack-app` `subscribe-pr` `pr-comments`(moved-to-plugin) |
| 浏览器扩展 + MCP + 订阅 | `chrome` |
| 内部 stub / 模板任务 | `torch`(reserved stub) `ant-trace`(disabled stub) `job`(TEMPLATES flag) |
| 2025 年度回顾插件novelty | `think-back` `thinkback-play` |

> INFRA-NA 与前一对齐线程([[project_cc_alignment_missing_commands_tools]])同一诚实原则:云/CI/IPC/订阅
> 结构缺失绝不造空壳对话框;若 khy 未来具备对应底座(如 remotedev 已有),再行接线。

## 进度

- [x] A1 `debug-tool-call`(最干净,优先)—— 叶子 `cli/debugToolCall.js`(配对/截断/渲染,诚实标注「result not stored」)+ 薄壳 `handlers/debugToolCall.js`(门控 KHY_DEBUG_TOOL_CALL,读当前 sessionId + buildConversationChain)+ 接线五件套(schema ROUTER_COMMANDS + slash 菜单 + router allowlist + switch case)。leaf 10/10 · 三守卫过 · bundled 三副本重建。
- [x] A2 `recap`(复用 sessionRecapService)—— 薄壳 `handlers/recap.js` 纯复用既有确定性 SSOT `sessionRecapService.generateRecap`+`formatRecap`(无模型也可用,诚实差异=对整段当前会话而非「away 边界」回顾)。门控 KHY_RECAP。空会话(sections:{})诚实降级只打 summary 不调 formatRecap(防抛)。handler 8/8 · 两守卫过 · bundled 三副本重建。
- [ ] A3 `copy`
- [x] A3 `copy` —— 纯叶子 `cli/copyReply.js`(parseCopyArgs/selectReply/extractCodeBlocks/buildCopyPayload,零 IO 确定性)+ 薄壳 `handlers/copy.js`(门控 KHY_COPY,读 chain → contentBlockUtils.contentToText 压平 → imageService.writeClipboardText 真剪贴板)。用法 /copy · /copy N · /copy code [N]。诚实:无 OSC52 走系统工具,无内容/无块/写失败如实告知绝不假装。leaf 10/10 · 三守卫过 · bundled 三副本重建。
- [x] A4 `rename`(复用 renameSession)—— 薄壳 `handlers/rename.js` 纯复用既有 SSOT `sessionPersistence.renameSession`(原子写+200 上限+索引刷新)。门控 KHY_RENAME。诚实差异=无参不偷起模型生成名,提示需显式给名;快照缺失如实报错。handler 5/5 · 两守卫过 · bundled 三副本重建。
- [ ] A5 `tag`
- [x] A5 `tag` —— 纯叶子 `cli/sessionTag.js`(normalizeTag/parseTagArgs/applyTags toggle 切换+去重保序,零 IO)+ 薄壳 `handlers/tag.js`(门控 KHY_TAG,读 loadSessionMeta.metadata.tags → 算完整新数组 → updateSessionMetadata 浅合并整组覆盖)。用法 /tag 列出 · /tag <名...> 打/去(同名再打=移除)。leaf 9/9 · 三守卫过 · bundled 三副本重建。
- [ ] A6(低优先·niche)`color`(并入 skin per-session)· `break-cache` · `heapdump`
- [x] A6 **全部实现(非 NA,均诚实可用)**——
  - [x] **heapdump** —— 纯叶子 `cli/heapDump.js`(dumpId 生成、`MemoryDiagnostics` 对象组装对齐 CC、措辞,确定性零 IO,now/sessionId/v8 统计量全注入)+ 薄壳 `handlers/heapdump.js`(门控 KHY_HEAPDUMP,`v8.writeHeapSnapshot` 落 `.heapsnapshot` + `fs.writeFileSync` 落诊断 JSON 到 `getDataDir('heapdump')`)。**背后逻辑**=堆快照只含 V8 堆,诊断 JSON 才区分泄漏在 V8 堆还是原生内存(mallocedMemory/detachedContexts/RSS)。leaf 7/7 · 三守卫过 · e2e 真落盘实证 · bundled 三副本重建。
  - [x] **break-cache** —— **非空壳**:khy 确实用 Anthropic 前缀缓存(`claudeAdapter`/`multiFreeService` 对静态前缀打 `cache_control:ephemeral`)。纯叶子 `cli/breakCache.js`(scope 解析、nonce 注释构造、事件日志聚合对齐 CC readStats、措辞)+ 薄壳 `services/gateway/breakCacheState.js`(marker/flag/jsonl 文件读写 + `consumeCacheBreakNonce` 消费侧)+ 薄壳 `handlers/breakCache.js`(once/always/off/--clear/status)。**消费接线**:`claudeAdapter` 组装系统提示前调 `consumeCacheBreakNonce(process.env)`,有 marker/flag 则把 nonce 注释前置到 `system` → 改前缀哈希 → 下次/每次调用 miss 缓存;once 模式消费后删 marker(一次性)。门控 KHY_BREAK_CACHE 默认开 → 关时 `consumeCacheBreakNonce` 恒空(网关字节回退绝不注入)。leaf+state 9/9 · 三守卫过 · bundled 三副本重建。
  - [x] **color** —— 纯叶子 `cli/sessionColor.js`(调色板=khy AGENT_COLORS 8 色、reset 别名、参数解析、`resolveAccent` 优先级 bash>memory>会话色>null、措辞)+ 薄壳 `cli/sessionColorState.js`(进程级活动色,首帧惰性从会话元数据播种)+ 薄壳 `handlers/color.js`(门控 KHY_SESSION_COLOR,`updateSessionMetadata(id,{color})` 持久 + `setSessionColor` 即时)。**消费接线**:Ink TUI `App.js:1505` 的 `accent` 改走 `resolveAccent({bashMode,memoryMode,sessionColor,env})` → 喂给既有 `PromptFrame` 的 `accent`(输入框边框 + `❯` 标记色)。门控关 → `resolveAccent` 忽略会话色 → accent 字节回退 null(=历史)。leaf+state 8/8 · TUI 304/304 无回归 · 三守卫过 · bundled 三副本重建。
- [x] B 批量补 CC 名别名(menu 补齐)—— 14 条 CC 名别名,**双机制**:① `cli/aliases.js` ALIAS_MAP(裸词分发,parseInput 在 switch 前解析,subCommand/defaultPositionals 全支持)② `commandSchema.js` BUILTIN_SLASH_COMMANDS slash 条目带 `route`(菜单可见 + /斜杠分发)。绝不另起 switch case —— 全部路由既有 canonical。映射:mode→persona · security-review→security scan · force-snip→snip · skills→skill list · skill-learning→skill learn · skill-search→skill search · local-vault→vault · local-memory→memory · provider→gateway config · reload-plugins→plugin reload · commit-push-pr→pr create · poor→effort low(defaultPositionals)· workflows→workflow · sandbox→sandbox-toggle。实证 parseInput:裸词 + /斜杠两形皆正确解析(`/poor`→effort args=['low']·`/skills`→skill list)。aliases+commandRegistry 回归 33/33 · model-hardcoding 过 · bundled 三副本(aliases.js+commandSchema.js)重建。**刻意不纳入**:`tui`(khy 默认全 TUI 无需开关)· `web-tools`(NL provider 配置 + web_search·非单命令可洁净别名)· `insights`(已存在完全等价)。
- [x] 工具面 diff(CC tools vs khy tools)—— 逐工具读 CC 源(packages/builtin-tools/src/tools/<Name>Tool/)对契约理解后分类:CC 60 个内置工具,khy 已有 63 个工具目录,**52 EQUIVALENT · 1 MISSING-IMPLEMENTABLE(MonitorTool)· 7 INFRA-NA**。
  - **EQUIVALENT 52**:Bash↔shellCommand · FileRead/Write/Edit↔readFile/writeFile/editFile · Glob/Grep · NotebookEdit · WebSearch/WebFetch/WebBrowser · Agent · Goal · LocalMemoryRecall · Vault · CtxInspect · Skill/DiscoverSkills · Config · Sleep · Snip · Brief · TodoWrite · Task{Create,Get,List,Update,Stop,Output} · Team{Create,Delete} · ScheduleCron/CronList/CronDelete · ReviewArtifact · VerifyPlanExecution · EnterPlanMode/ExitPlanMode · EnterWorktree/ExitWorktree · SyntheticOutput=StructuredOutput · SearchExtraTools↔toolSearch · Execute(toolSearch select 架构吸收)· PowerShell · MCP/ListMcpResources/ReadMcpResource/McpAuth · AskUserQuestion · SendUserFile · PushNotification↔PushNotify(用户配置 ntfy/Bark/Discord/Slack/webhook 直连·比 CC bridge-only 更诚实)· ListPeers↔MeshPeer · SendMessage。
  - **INFRA-NA 7**:ArtifactTool(云 artifact store·khy 已本地语义化写 getDataDir 不伪造 hosted URL)· RemoteTrigger(Anthropic 云 triggers API + OAuth)· SubscribePR/SuggestBackgroundPR(KAIROS 云 webhook/后台 PR agent·CC 自身即 stub)· TerminalCapture(需 PTY 复用器·CC+khy 皆空 stub)· OverflowTest/Tungsten(CC 内部 auto-gen stub·khy 无此目录=正确缺席)。
  - **MISSING-IMPLEMENTABLE 1 → DONE**:见下。
- [x] **MonitorTool 真后台化(唯一真缺口)**—— 旧 `execute()` 阻塞到 `proc.on('close')` 整段返回,却在 prompt 里自称"runs in the background"=**契约自相矛盾**(CC Monitor 核心价值=非阻塞 + 实时可读 outputFile + 退出通知,用于 tail -f / 轮询 / watch)。修:门控 `KHY_MONITOR_BACKGROUND` 默认开 → 非阻塞,stdout/stderr 流式写 `getDataDir('monitor')/<taskId>.log`(用 Read 工具随时查看·实证读到 early 时进程仍 running),注册进 `backgroundShells`(与 shellCommand run_in_background 同一 SSOT map)由 toolUseLoop 既有 drain 产 `<task_notification>`,立即返回 `{taskId, backgroundTaskId, outputFile}`。退出后 `collectBackgroundResults` 经 `summarize(entry.result.output)` 产一条一次性通知。**门控关 → 逐字节回退旧阻塞行为**(返回 exitCode+output 内联·无 taskId/outputFile)。CC 无 timeout(monitor 跑到自退)→ 后台模式仅当显式传 timeout 才 kill,否则不限时。`isConcurrencySafe()` 随门控(开 true 关 false)。node:test 8/8(含非阻塞<1s 返回·实时可读·失败 exitCode·门控关回退·drain 一次性)· agent-rules/model-hardcoding 过 · bundled 三副本重建。
- [ ] 前端逐项对齐 + TUI 学习 —— 进行中(注重背后逻辑·逐刀)
  - [x] **刀1「% until auto-compact」上下文警告状态机** —— 对齐 CC `autoCompact.ts::calculateTokenWarningState` + `TokenWarning.tsx` 的**背后逻辑**(非外观):① 何时显示=仅警告带内(`used >= 阈值-20000`,不从首 token nag)② 100% 分母=auto-compact **阈值**(真触发点)非裸窗口 ③ 状态机=开则 dim「% until auto-compact」/关则着色「Context low (…) · Run /compact」。修两处真 bug:错误信号(`sessionTokens.total` 累计无界 → `contextWindow.used` 当前占用)+ 错误分母(裸 limit → 阈值)+ 恒显示(→ 警告带门控)。**诚实**:countdown 阈值参数化为 khy 真实触发比 `compactPipeline.AUTOCOMPACT_THRESHOLD`(0.8,已导出)而非硬抄 CC 数字 → 0% 对齐真压缩事件。纯叶子 `cli/contextWarning.js`(`calculateTokenWarningState`/`buildContextWarning`)收敛 repl 两渲染点(`_renderPermissionBar` 着色 + `_buildPermissionBarText` 文本)。门控 KHY_CONTEXT_WARNING 默认开·关→逐字节回退旧恒显示。leaf 11/11 · 三守卫过 · bundled 三副本重建 · GUARDS 红线节。
  - [x] **刀2 spinner 计时/token byline 30s 揭示门控** —— 对齐 CC `Spinner/SpinnerAnimationRow.tsx` 的**背后逻辑**(非外观):`SHOW_TOKENS_AFTER_MS=30000`,`wantsTimerAndTokens = verbose || hasRunningTeammates || effectiveElapsedMs > 30000` → 前 30s spinner 只显**动词**,30s 后才露 `Xm Ys · ↓ N tokens` byline(快回合永不闪计时器);effort/thinking 后缀只受**宽度**门控不受 30s 钟。修真 bug:`spinner.js::_renderInner` `parts = [elapsedStr]` 起手 + token 计数 `>0` 就推,**全无 elapsed 门控** → 第一帧就闪计时+token。纯叶子 `cli/spinnerMeta.js`(`shouldShowTimerAndTokens`/`buildStatusParts`)收敛 `spinner.js` byline 组装,顺序恒 legacy `[timer,↑in,↓out,effort]`,空括号已修(前 30s 无 effort → 只显动词无空 `()`)。门控 KHY_SPINNER_META_GATE 默认开·关→恒显示逐字节回退。leaf 9/9 · 三守卫过 · bundled 三副本重建 · GUARDS 红线节。
  - [NA] **候选刀3 spinner 提示阈值(clear@30min/btw@30s)** —— 诚实 NA:khy `spinner.js::_tipShown` 是无消费者**死旗**(设置后从不渲染任何 tip),对齐 CC tip 阈值属 net-new tip 渲染特性而非显示逻辑对齐 → 不造空壳(守 khy 诚实红线)。
  - [x] **刀3 成本金额显示精度 SSOT(ccFormat.ccFormatCost)** —— 对齐 CC `cost-tracker.ts::formatCost` 的**背后逻辑**(非外观):**幅度自适应精度**,一个 `0.5` 阈值切换位数——`cost > 0.5 → round(cost,100).toFixed(2)`(大额 2 位)/ `cost ≤ 0.5 → toFixed(4)`(微额 4 位,防亚分成本 `¥0.005` 四舍五入塌 0)。CC 是 `formatTotalCost/formatModelUsage/BuiltinStatusLine` 唯一成本 SSOT;khy 此前**三处各写一套**:`hudRenderer.js` 状态行 `0.01` 阈值、HUD 面板**硬编码** `toFixed(4)`/`toFixed(2)`、`router.js` /admin **硬编码** `toFixed(4)` → 大额会话误显 `￥123.4567` 过度精度。补齐 `ccFormat.js` 族最后缺位的成本格式化器(忠实移植 CC `formatCost`+`round`,**不带货币符号**返回纯数字串,`¥`/`$` 由 call-site 拼,同族 `ccFormatTokens` 惯例)。收敛三 call-site(状态行 ×1 + HUD 面板 session/月 ×2 + /admin 总费用 ×1)。门控 KHY_CC_FORMAT(同族同门)默认开·关→返回 call-site 自带 legacy 逐字节回退各自旧 toFixed 口径。e2e:`$17` 会话面板 `￥123.2500`→`￥123.25`(关→`￥123.2500`)、微额 `$0.001`→`￥0.0073` 两态一致。leaf 10/10 · 既有 ccFormat 族回归 37/37 · 三守卫过 · bundled 三副本重建 · GUARDS 红线节。
  - [x] **刀4 HUD token 计数走 ccFormatTokens SSOT** —— 对齐 CC `format.ts::formatTokens`(Intl 紧凑:`12345→12.3k`、`1000→1k`、`1.5M→1.5m`)。真缺口=`hudRenderer.js` 用**模块内私有本地 `fmtTokens`** 偏离:`>=10000→toFixed(0)k` 丢小数(`12k`)、`>=1000→1.0k` 多余 `.0`、**无兆单位**(`1500k`)——而 khy 其余**所有** token 显示面(App/FooterBar/Spinner/CompactionProgress/useQueryBridge/turnStats)早已走 `ccFormatTokens`,HUD 是唯一漏网。修:HUD `fmtTokens` 在门控开时委派 `ccFormatTokens`(`''`/非有限→落本地兜底),5 个 call-site 经此单点统一(状态行 session tokens + compactTokens + 面板 Context used/limit + agent tokens)。门控 KHY_CC_FORMAT(同族同门)默认开·关→逐字节回退本地旧三分支。e2e:面板 Context `12.3k / 200k`(关→`12k / 200k`)。test 3/3(门控开 12.3k·关回退 12k·SSOT vs 本地发散点 1000/123456/1500000)· 三守卫过 · bundled 三副本重建 · GUARDS 红线节。
  - [x] **刀5 HUD elapsed 时长走 ccFormatDuration SSOT** —— 对齐 CC `format.ts::formatDuration`(与 `turnStats`/spinner `_formatElapsed` 同一真源)。真缺口=`hudRenderer.js` 本地私有 `fmtDuration` 偏离且**丢秒**:`>=60s→"${min}m"`(`90s` 显 `"1m"` 丢 30s)、小时态 `"${h}h${m}m"`(无空格丢秒,`1h1m1s→"1h1m"`)——而 khy 其余 elapsed 面早已带秒(`turnStats` 走 `ccFormatDuration`、spinner `_formatElapsed` 显 `"Xm Ys"`),HUD 是唯一丢秒漏网。修:HUD `fmtDuration` 在门控开时委派 `ccFormatDuration(ms,{hideTrailingZeros:true})`(`''`/非有限→落本地兜底),4 个 call-site(工具/压缩/会话/活动工具 elapsed)经此单点统一。`hideTrailingZeros` 既忠实 CC(`90s→"1m 30s"` 保秒、`1h1m1s→"1h 1m 1s"` 带空格)又保 HUD 紧凑意图(`60s→"1m"` 不显 `"1m 0s"`)。门控 KHY_CC_FORMAT(同族同门)默认开·关→逐字节回退本地旧三分支(丢秒口径)。e2e:面板 Session 行 90s `"1m 30s"`(关→`"1m"`)。test 4/4(门控开 90s 保秒/60s 隐 0s+小时态带空格/门控关回退丢秒+无空格/SSOT vs 本地发散点直对照;经公开 `renderHudPanel` 验证因 `fmtDuration` 私有,读真 `sessionStart` 后 mock `Date.now`)· 三守卫过 · bundled 三副本重建 · GUARDS 红线节。
  - [x] **刀6 剪贴板图片大小展示走 ccFormatFileSize SSOT** —— 对齐 CC `format.ts::formatFileSize`(续 health/storage/multimodal/aiUploadStore/archive 字节展示面统一收敛,见 `fileSizeSsot`/`fileSizeMediaSsot` 两测试)。真缺口=两个**图片大小**展示面仍各自发散且缺进位:`repl.js` **5 个** call-site(「图片提示」剪贴板分析结果行)`(sizeBytes/1024).toFixed(0)KB` **恒 KB**(`5MB` 图误显 `"5120KB"`、`1.5MB` 误显 `"1536KB"`、`800B` 塌 `"1KB"`)、`imageService.printImagePreview` 2 分支 `≥1MB→"X.XMB" 否则 "XKB"`(无 `bytes`/`GB` 档)。修=各加门控感知封装 `repl._formatImageSize` / `imageService._imageSizeStr`(镜像既有 `atMentionInject._formatMentionSize` / `multimodalInputService._formatBytes` 模式),门控开委派 `ccFormatFileSize`(`''`→落本地兜底),5+1 call-site 经此统一。门控 KHY_CC_FORMAT(同族同门)默认开·关→逐字节回退**各 call-site 自己的**历史口径(repl 恒 `toFixed(0)KB`·imageService 2 分支带尾随 `.0`)。e2e:`5MB`→`"5MB"`(关 repl→`"5120KB"`/imageService→`"5.0MB"`)、`1.5MB`→`"1.5MB"`(关 repl→`"1536KB"`)、`800B`→`"800 bytes"`(关→`"1KB"`)。test 6/6(两 call-site 门控开 = `ccFormatFileSize` 逐字节·CC 形态 MB/GB 进位+bytes 词+去 `.0`·门控关各自旧口径·开关唯一分歧=SSOT 进位;经导出 `_formatImageSize`/`_imageSizeStr` 验证,镜像 `fileSizeMediaSsot` 导出私有 helper 惯例)·既有 file-size SSOT 回归 32/32(fileSizeMedia+fileSize+atMentionInject)·守卫过(agent-rules/model-hardcoding imageService 净·repl.js 1 warn=`no-opaque-status:3175` 预存无关)·bundled 三副本重建(imageService+repl 各 `_imageSizeStr`/`_formatImageSize` 路由实证)·GUARDS 红线节。
  - [x] **刀7 `/cost`·`/usage` 报表 token 计数走 ccFormatTokens SSOT** —— 对齐 CC `/cost`(`cost-tracker.ts::formatModelUsage` + `Stats.tsx` "Total tokens" 一律 `formatNumber` 紧凑记数 `1234567→"1.2m"`),续刀4 token 收敛。真缺口=khy `/cost`·`/usage` 报表子系统是**唯一漏网**:`tokenUsageService.js` **三处**局部 `fmtNum = n.toLocaleString('en-US')`(`formatInlineSummary`/`formatUsageReport`/`formatCostReport` 共 ~15 行 token 计数)+ `router.js` **两处**内联 `.toLocaleString()`(`/cost today` `:2448`、`/cost history` `:2455`)→ 全产**全分隔符** `"1,234,567"`,既偏离 CC 报表逻辑,又与 khy 自身其余 **6+ 个** token 面(HUD/Spinner/Footer/Compaction/turnStats,早走 `ccFormatTokens`)不一致。修=新增 `tokenUsageService._fmtTokenCount(n, legacy, env)`(镜像 `ccFormatCostOr(value,legacy,env)` 族签名,门控开委派 `ccFormatTokens`、`''`→落 legacy),三处 `fmtNum` 改 `(n)=>_fmtTokenCount(n, n.toLocaleString('en-US'))`(call-site 不动)、router 两处经 `tokenSvc._fmtTokenCount(x, x.toLocaleString())`。门控 KHY_CC_FORMAT(同族同门)默认开·关→逐字节回退**各 call-site 自带 legacy**(旧 `toLocaleString` 全分隔符)。**诚实精度权衡**=报表/额度行紧凑后逐位精度由并列百分比承载(`剩余 1.2m / 2m (62%)`),这正是 CC `/cost` 既定选择;需全精度者 `KHY_CC_FORMAT=off` 即逐字节取回旧 `1,234,567` 报表(门控即逃生舱,绝不丢旧行为)。e2e:`formatCostReport` ON `输入 1m / 合计 1.2m`·OFF `1,000,000 / 1,234,567`·inline ON `本次 1.2m · 剩余 0/100k`·OFF `1,234,567 · 0/100,000`。test 6/6(门控开=`ccFormatTokens` 逐字节·紧凑具体形态·门控关=`toLocaleString` 全分隔符逐字节·开关唯一分歧·防呆非有限落 legacy·默认开)·既有 token/usage jest 回归 55/55(tokenUsageService+usageFormatter+usageTracker)·守卫过(agent-rules/model-hardcoding tokenUsageService+router 净)·bundled 三副本重建(各 `_fmtTokenCount` ON `1.2m`/OFF `1,234,567` + router require 解析)。GUARDS 红线节。
  - [x] **刀8 markdown blockquote 正文样式走 italic·正常亮度 SSOT(离开 ccFormat 族·新领域 markdown 渲染)** —— 对齐 CC `src/utils/markdown.ts` 的 `case 'blockquote'` **背后逻辑**(非外观):`const bar = chalk.dim(BLOCKQUOTE_BAR); ... ${bar} ${chalk.italic(line)}`,且源码有**逐字注释**`"Keep text italic but at normal brightness — chalk.dim is nearly invisible on dark themes."`——CC 刻意把竖条 dim、**正文 italic 且正常亮度**,因深色主题下 `chalk.dim` 几乎不可见。真缺口=khy `markdownRenderer.js:601` blockquote 正文 `c().dim(body)`——竖条 dim(与 CC 一致)但**正文也被 dim 掉**=逐字复刻了 CC 这条注释**专门要避免**的可读性 bug(深色主题下 blockquote 正文几乎不可见)。该渲染分支无条件、经 `_renderMarkdownLiteInner` → 导出的 `renderMarkdownLite`/`renderMarkdownStreaming`(助手输出主 markdown 渲染器)上屏=真用户可见。修=新纯叶子 `cli/blockquoteStyle.js`(`blockquoteBodyStyleEnabled(env)` 门控梯 + `blockquoteBodyStyle(env)` 返回 `'italic'`(门控开·CC)/`'dim'`(门控关·历史回退);零 IO 零业务 require,样式应用留 call-site=同 `orderedListAlign` 对齐算法留叶子、着色留 renderer 的分工),call-site 改 `c()[blockquoteBodyStyle()](body)`。**刻意不纳入**:竖条始终 dim(与 CC 一致,本就对)·`│` 竖条字形(CC `▎`)与 2 空格缩进=khy 主题选择非本刀。门控 KHY_BLOCKQUOTE_STYLE 默认开·关→逐字节回退 `c().dim(body)`。e2e:`> An important caveat.` 门控开 `  [dim │] [\x1b[3m italic body]`(对齐 CC)·关 `  [dim │] [\x1b[2m dim body]`(历史字节回退)。test 6/6(叶子门控梯·`'italic'`/`'dim'` 决策·唯一分歧锁定·渲染真 ANSI `\x1b[3m` 开/`\x1b[2m` 关/默认开 italic)·markdown 渲染回归 33/33(markdownStreaming/CodeWrap/TableCellWrap/Tighten/orderedListAlignRender/markdownEmphasis)+ aiRenderer.formatting jest 40/40·三守卫过(leaf-contract blockquoteStyle·agent-rules/model-hardcoding blockquoteStyle+markdownRenderer 净)·bundled 三副本重建(各 leaf 解析 + ON italic/OFF dim 实证)。GUARDS 红线节。
  - [x] **刀9 markdown 行内下划线强调 `_italic_`/`__bold__` 走 CommonMark 词内守卫 SSOT(续 markdown 渲染域·新类别:行内强调定界符)** —— 对齐 CC `src/utils/markdown.ts`:CC 用 `marked` 词法器按 CommonMark 解析行内强调(`em→chalk.italic`/`strong→chalk.bold`),**同时识别** `*`/`**` 与 `_`/`__` 两套定界符并套 flanking 规则(`_emphasis_` 斜体而 `my_var_name` 词内下划线**字面**)。真缺口=khy `markdownRenderer.js:582-586` 行内强调链**只认星号**(`***`/`**`/`*`),完全无下划线规则 → `_italic_`/`__bold__`(标准 CommonMark·模型常产出)连定界符原样上屏(`_emphasized_` 字面显示·强调丢失)。经 `renderMarkdownLite`(`aiRenderer.js:523` 经典 REPL + Ink TUI `Transcript.js`/`StreamingBlock.js`)真上屏。修=新纯叶子 `cli/underscoreEmphasis.js`(`underscoreEmphasisEnabled` 门控梯 + `applyUnderscoreEmphasis(text,styler)`:三定界符 `___`/`__`/`_` 长在前,**承重=词内守卫**——每定界符外侧 `(?<!\w)`/`(?!\w)` + 内侧 `(?=\S)`/`(?<=\S)`,使 `_foo_`→样式而 `some_function_name`/`_a_b_`→字面;chalk 应用经 `styler` 注入留 call-site)。接线=星号链跑完后、行内代码占位符还原(`\x00IC…\x00` 无下划线·`code_with_under` 受保护)**之前**插入门控 `if(underscoreEmphasisEnabled())` 步,`styler={italic:c().italic, bold:c().bold, boldItalic:c().bold.italic}`。**承重设计点**=词内守卫正是 khy 当初省略下划线的原因(防 `snake_case` 误斜体),非猜测而是 CommonMark intraword 精确规则。**诚实边界**:行级正则在已注入 ANSI 的文本上跑,下划线强调**紧邻**其他行内样式(无空格)时外侧 `m`(ANSI 结尾)会挡住边界守卫=罕见边角,正则渲染固有限制,如实接受不伪装 AST。门控 KHY_UNDERSCORE_EMPHASIS 默认开·关→跳过该步→下划线原样逐字节回退。e2e:`_emphasized_`→`\x1b[3memphasized`(CC)·`__strong__`→`\x1b[1mstrong`·`some_function_name`→字面·关→`_emphasized_` 字面。test 13/13(门控梯·基本三态·行首尾边界·**承重 snake_case/词内字面**·内侧空白守卫·混合·防呆·渲染真 ANSI 开/snake 不斜体/关字节回退/默认开/星号不受影响)·markdown 渲染回归 39/39(streaming/codeWrap/tableCellWrap/tighten/orderedListAlignRender/emphasis/blockquote)+aiRenderer.formatting jest 40/40·三守卫过·bundled 三副本重建(各 leaf 解析+ON italic+snake 字面+OFF 字面实证)。GUARDS 红线节。
  - [NA] **候选刀(有序列表按嵌套深度切标号 1./a./i.)诚实 honest-NA** —— CC `markdown.ts` `getListNumber`/`numberToLetter`/`numberToRoman`:depth 0/1→数字、depth 2→字母 `a.b.c.`、depth 3→罗马 `i.ii.iii.`,且 CC 走 marked **AST** 真实深度由 token 树参数 `listDepth` 携带(非缩进推断)。khy `markdownRenderer.js:596` 有序列表是**无状态行级正则** `^(\s*)(\d+)\.\s+`,`orderedListAlign.js` 仅按缩进串相等分组,`mermaid._renderNestedListTrees` 只吃 bullet 不吃 ordered → khy **无任何结构/AST 深度信息**。真分歧确凿(`1./1./1.` vs CC `1./a./i.`)但**不可忠实实现**:markdown 每级缩进宽度可变且上下文相关(`1.` 下缩进 3 vs `10.` 下缩进 4 同深度不同缩进·blockquote 内整体缩进的顶层列表会被误升级),任何缩进→深度启发式都会**误渲染常见情形**(给本是顶层但缩进的列表打 `a./i.`),严格劣于现状朴素数字。忠实版需把整个渲染器移植到真 markdown AST=独立大型架构变更非一刀。守诚实红线**绝不**造模糊缩进推断,记 NA。
  - [x] **刀10 diff add/remove 行背景色条「填充到终端整宽」走 SSOT(新领域:diff 渲染布局·非 markdown)** —— 对齐 CC `src/components/StructuredDiff/Fallback.tsx::formatDiff` **背后逻辑**:CC 对每一 diff 行算 `const padding = Math.max(0, safeWidth - contentWidth)` 再在 `backgroundColor` 的 `<Text>` 内追加 `{line}{' '.repeat(padding)}`(word-diff 分支同理 `padding = Math.max(0, width - usedWidth)`),使红/绿背景条**一路铺到终端右缘**=CC 标志性「实心整行色条」。真缺口=khy 所有 diff 路径只把背景铺到**文本末尾**,右侧参差(`12 - return foo;` 后即默认背景,多行 diff 呈锯齿右缘)。修=新纯叶子 `cli/diffFullWidth.js`(`diffFullWidthEnabled(env)` 门控梯 + `diffRowPadCount(used,total)` 纯算术镜像 CC `Math.max(0,width-content)`+防呆 + `diffRowPadSpaces`;零 IO、终端宽度/显示宽度测量/chalk 全留 call-site),接线**经典 ANSI 路径** `diffRenderer.js`:`renderDiff`(/diff 命令,经 `router.js:1360`/`repl.js:3870,4448` `console.log` 直出)+ `renderStructuredDiff`(经典 REPL 工具 write-diff)两函数共 8 处 bgHex 行,门控开时把纯文本已用宽度(`displayWidth` CJK 感知·剥 ANSI)补足背景空格到 `process.stdout.columns`;context(无背景)/`---`/`+++` 头不补。**关键诚实边界(为何不接 Ink TUI 默认路径)**=khy TUI 用 vanilla `ink@6.8.0`,其 `build/output.js:174` 渲染每行无条件 `styledCharsToString(...).trimEnd()` **裁掉带背景色的尾随空格**→在 TUI 追加尾随空格是**静默无效**(色条仍参差);CC 用的是 `@anthropic/ink` 分叉(不做此 trim)才能整宽。故 TUI 整宽色条记 **honest-NA**(改/分叉 ink 属架构变更非一刀,绝不上屏一个被 trim 吃掉的死「修复」)——能力**只**接经典 ANSI 路径(`console.log` 直出无 ink trim,尾随背景空格存活·已实证 reset `[39m[49m` 落在补白空格之后=背景覆盖)。门控 KHY_DIFF_FULL_WIDTH 默认开·关→不补=逐字节回退参差色条。e2e(FORCE_COLOR=3·columns=80):`renderStructuredDiff` add/del 行可见宽度补到 80(context 行 7 不补)·`renderDiff` +/- 行 80(`+++` 头 5 不补)·cols=120→120(跟随终端)·关→add 行 7(legacy 参差)。test 11/11(门控梯·`diffRowPadCount` 算术+防呆 NaN/Inf/小数 floor+used≥total→0·`diffRowPadSpaces` 关空串开空格串·集成开整宽/context 不补·背景覆盖补白·关字节回退·默认开·renderDiff 头不补+跟随 columns)·回归 writeDiff(node:test)25/25(regex `/- b/` `/\+ B/` + CJK/emoji `.includes` 不受补白影响)+ab_test_interactivity 50/50+editStatLine 11/11+TUI 304/304(ToolLines 未动)+ab_interactivity_v2 60/61(GAP-2 `_formatToolResult` 预存失败无关)·三守卫过(leaf-contract diffFullWidth·agent-rules/model-hardcoding diffFullWidth+diffRenderer 净)·bundled 三副本重建(各 leaf 解析+整宽渲染实证)。GUARDS 红线节。
  - [x] **刀11 markdown 行内链接 `[text](url)` 展示形态走 SSOT(回 markdown 渲染域·新类别:链接展示决策)** —— 对齐 CC `src/utils/markdown.ts` link case **背后逻辑**两条刻意规则:① `// Prevent mailto links from being displayed as clickable links` → `const email = token.href.replace(/^mailto:/,''); return email`(mailto 剥 scheme 只显裸邮箱纯文本);② `if (plainLinkText && plainLinkText !== token.href) createHyperlink(text); else createHyperlink(href)`(展示文本===URL/空 → URL 只显一次不重复)。真缺口=khy `markdownRenderer.js:588` 通用 `[text](url)` → `${link.underline(text)} ${dim(`(${url})`)}`:**①** `[Email](mailto:foo@bar)` 渲成 `Email (mailto:foo@bar)` 泄 `mailto:` scheme 且邮箱埋进 dim 括号;**②** `[https://x.com](https://x.com)` 渲成 `https://x.com (https://x.com)` 自我重复。修=新纯叶子 `cli/markdownLink.js`(`markdownLinkDisplayEnabled(env)` 门控梯 + `planLinkDisplay(text,url,env)` 返展示计划 `{kind:'plain'|'url-only'|'text-url',…}`;零 IO、零业务 require、确定性,chalk 全留 call-site),接线 `markdownRenderer.js` 的 `[text](url)` replace:`plain`→裸邮箱纯文本(对齐 CC 不着色)、`url-only`→`link.underline(url)` 只显一次、`text-url`→历史 `text (dim url)`。**诚实边界(刻意不纳入)**=khy 用显式 `text (dim url)` 而非 CC 的 OSC-8 可点击超链接(`createHyperlink`),在任何终端都能看清真实目标、不依赖 OSC-8 能力,是**有意渲染选择**而非缺陷 → **不**引入 OSC-8(行为变更非显示对齐);空展示文本 `[](url)` 在 khy 永不命中既有正则(`[^\]]+` 要求≥1 字符),叶子仍防御性并入 url-only。门控 KHY_MARKDOWN_LINK_DISPLAY 默认开·关→一律 `text-url` 逐字节回退(连 mailto 也回退到泄 scheme 旧行为)。test 11/11(门控梯·planLinkDisplay mailto 剥 scheme+大小写不敏感/text===url 去重/空白防御/普通 text-url/门控关一律回退/null·undefined 防呆·集成经 `renderMarkdownLite` mailto 绝不泄 scheme+URL 只显一次+普通仍 text(url)+门控关回退)·回归 markdownTighten/CodeWrap/TableCellWrap/TableWrap/Emphasis 34/34+markdownStreaming 8/8(全树无既有测试断言链接渲染→默认开无破坏)·三守卫过(leaf-contract markdownLink·agent-rules/model-hardcoding markdownLink+markdownRenderer 净)·bundled 三副本重建(各 leaf 解析+mailto 无 scheme 实证)。GUARDS 红线节。
  - [x] **刀12 子 agent / 工具追踪器统计行 `Done (N tool uses · X tokens · Ys)` 三分段构造走 SSOT(新类别:agent 统计行措辞·非 markdown)** —— 对齐 CC `packages/builtin-tools/src/tools/AgentTool/UI.tsx:359-363` **背后逻辑**:`const result = [ totalToolUseCount===1?'1 tool use':`${n} tool uses`, formatNumber(totalTokens)+' tokens', formatDuration(totalDurationMs) ]; `Done (${result.join(' · ')})``——三件套刻意走 `=== 1` 单数守卫 + `formatNumber`(注意是 formatNumber 而非 formatTokens,保留尾随 `.0`)+ `formatDuration`(带 h/d 进位)。真缺口=khy **5 处各自手写**这三段(`agentTreeView.formatStats`、`toolDisplay` agent 行 / `renderAgentDone` / ToolUseTracker、`agentRenderer.renderAgentSummary`):**①** 恒复数 `${n} tool uses`(单数误显 `1 tool uses`);**②** token 手写 `/1000 .toFixed(1)k`(≥1M 显 `1500.0k tokens` 而非 CC `1.5m`,`2150` 因 toFixed 截断显 `2.1k` 而非 CC 四舍五入 `2.2k`);**③** 时长手写 min/sec **无小时进位**(`3735000ms` 显 `62m 15s` 而非 CC `1h 2m 15s`,树视图 `135.0s`/`2.1s` 而非 `2m 15s`/`2s`)。三段对应的 CC 忠实移植 `ccFormatNumber`/`ccFormatDuration` **早已**在 `cli/ccFormat.js` 落地并接到相邻面(turnStats/hudRenderer/Spinner),这些 stat 行只是**漏接**(faithful-port miss,非新逻辑)。修=新纯叶子 `cli/agentStatLine.js`(三个 `*Or(value, legacy, env)` 函数,镜像 ccFormat 的 `ccFormatCostOr` 形状;门控开→CC 形态,门控关→原样返回 call-site 传入 legacy;纯叶子链 `agentTreeView→agentStatLine→ccFormat`,零 IO、仅 require 同族纯叶子 ccFormat),复用 `KHY_CC_FORMAT` 门控(本就是 formatNumber/formatDuration 家族总开关)。接线四处 call-site(`agentTreeView.formatStats` 内部消费、`toolDisplay` 三处 agent 行/renderAgentDone/ToolUseTracker、`agentRenderer.renderAgentSummary`)各传自带 legacy 串(各 call-site 历史 k/秒口径不同→由 call-site 传 legacy 而非叶子写死,字节回退绝不串味)。**诚实边界**=各 call-site 前缀字形(`·`/`└`/`Done`)+chalk dim/green 着色留各 call-site 样式层不动(叶子只产纯文本三分段);计数源(toolCalls/tokens/elapsed 累加)是另一层不碰。test 13/13(单数守卫开 `1→1 tool use` 关 `1→1 tool uses`/token `1500000→1.5m`·`2150→2.2k`·`1000→1.0k`·`500→500 tokens` 无 k·门控关回退/时长 `3735000→1h 2m 15s`·`135000→2m 15s`·`2100→2s`·门控关回退/默认开)·**回归**:agentTreeView(jest)34/34(更新 1 处 legacy 断言 `elapsed:2100` 数字态 `2.1s→2s`=默认开 ccFormatDuration 整秒下取整新口径;字符串态 `1.8s` 透传不变、token 2100→`2.1k` 不变)+agentStatLine 13/13+toolDisplay(CommentLabel/ShellJson)9/9+ab_test_interactivity 50/50+TUI 304/304+ab_interactivity_v2 GAP-2 预存失败无关·三守卫过(leaf-contract agentStatLine+agentTreeView·agent-rules/model-hardcoding 四文件净)·bundled 三副本重建(各 leaf 解析+`1 tool use`/`1.5m tokens`/`1h 2m 15s` 实证)。GUARDS 红线节。
  - [x] **刀13 运行中子 agent「活动占位」liveness 走 SSOT(同 agent 树域续刀:状态文本·非措辞数字)** —— 对齐 CC `src/components/AgentProgressLine.tsx::getStatusText` **背后逻辑**:`if (!isResolved) return lastToolInfo || 'Initializing…'`——任何**未完成**(状态 RUNNING)的 agent **一律**给一条状态行:起了工具显工具行,**未起工具/未吐 prose 也显 `Initializing…`**,父级绝不会看到「只有名字、下面空一片」的行误读成卡死。真缺口=khy `agentTreeView.detailText` 在「running+currentTool」「running+currentText」两分支后直接 `return agent.detail || ''`→刚 spawn 还没起首个工具的 running agent **detail 为空** → 无 detail 子行 → 渲成裸 `├ name`(经典 agentRenderer + ink AgentTree 两前端皆然,因共用 detailText SSOT)。修=`detailText` 末尾补一条门控分支 `status===RUNNING && !agent.detail && agentInitStatusEnabled(env)` → `'Initializing…'`(`…`=U+2026 与 CC 逐字节一致);**只对 RUNNING**(PENDING 保裸名+○ 空心点表意·terminal 显 outcome),currentTool/currentText/detail **永远优先**。`detailText(agent, env)` 新增可选 env 第二参(默认 `process.env`,`buildAgentTreeRows` 内部调用沿用默认不破)。**诚实边界**=不动 tree glyph(CC `├─`/`└─` 双字带横杠 vs khy `├`/`└` 单字=纯字形美学·logic 极薄·不在本刀)·不动着色(各前端样式层)·只补「running 无活动→占位」这一条有明确 logic 的状态行。门控 KHY_AGENT_INIT_STATUS 默认开·关→detailText 末尾逐字节回退历史 `''`。test:agentTreeView(**jest**)35/35(新增 liveness 描述块:running+idle→`Initializing…`/tool·text·outcome 优先/PENDING 仍裸名/ERROR 显 Failed/门控关 `off`·`0` 回退 `''`;更新原 `detailText({status:RUNNING})` 断言 `''→'Initializing…'`=默认开新口径)·回归 buildAgentTreeRows 既有断言用 `.find`/`.filter('agent')` 不钉总行数→running-idle 多出的 detail 行无破坏·TUI 304/304·ab_test_interactivity 50/50·三守卫过(leaf-contract/agent-rules/model-hardcoding agentTreeView 净)·bundled 三副本重建(各 leaf 解析+`Initializing…` 开/`''` 关实证)。GUARDS 红线节。
  - [x] **刀14 ink TUI live spinner 计时+token「30s reveal-gate」收口走既有 SSOT(同 spinner 域续刀:复用纯叶子非另起)** —— 对齐 CC `src/components/Spinner/SpinnerAnimationRow.tsx` **背后逻辑**:`SHOW_TOKENS_AFTER_MS = 30_000`,`wantsTimerAndTokens = verbose || hasRunningTeammates || effectiveElapsedMs > 30000`,`showTimer`/`showTokens` 都要它 → 前 30s spinner 只显**动词/glyph**,30s 后才浮出计时+token meta(快回合永不闪计时器)。此 reveal 判据**早有纯叶子 SSOT** `cli/spinnerMeta.js`(`shouldShowTimerAndTokens`/`isEnabled`,门控 KHY_SPINNER_META_GATE),但**只接了经典 REPL spinner** `cli/spinner.js`——ink TUI 这条 `tui/ink-components/Spinner.js::buildSpinnerMeta` 此前**从第 1 秒即显** `· 2s · ~40 tok`(无阈值)=与 CC **及 khy 自己的经典 spinner** 双双不一致的真缺口。修=ink `buildSpinnerMeta` 起手**消费同一个叶子**:`shouldShowTimerAndTokens({ elapsedMs: sec*1000, gateEnabled: sm.isEnabled(env) })` 为 false → 返 `''`(require 包 try → 叶子不可用跌穿到照常显示,绝不因加载失败静默吞 meta);与经典 spinner **共用同一门控 + 同一 30s 阈值常量**,**绝不**在 ink 侧另写阈值常量或另起门控(早先草案曾内联 `SHOW_META_AFTER_MS`+新门控 `KHY_SPINNER_META_THRESHOLD`,自检发现重复既有 SSOT 后否决,收敛到复用)。**诚实边界**=khy 这条路径无 verbose/running-teammates 概念 → 仅 30s 钟生效(诚实只读叶子结论,绝不伪造旁路);ccFormat 数字/时长路由(刀早已落)与本 reveal-gate 正交,两门控独立。test:inkRenderSmoke(TUI harness)新增 G-C11(默认开 5/29/30s→`''` 隐藏·31s→`· 31s · ~340 tok` 浮出·90s→ccFormat `1m 30s`/`1.2k`·门控关 `0`/`off`→第 1 秒即显·与 ccFormat 门控独立两关→即显+原始整数);G-C8 ccFormat-路由断言改 pin `KHY_SPINNER_META_GATE:'off'` 隔离阈值(5s/12s 低秒态专测格式化口径);G-B 渲染帧测 `elapsedSec` 12→35(>30s 才让 meta 上屏);stall 测(elapsedSec:9)断言 `等待响应` 与 metaStr 正交不受影响。TUI 305/305(304 基线+G-C11 新测)·三守卫过(leaf-contract/agent-rules/model-hardcoding Spinner.js 净·新增 require 同族纯叶子 spinnerMeta)·bundled 三副本重建(各副本解析+≤30s 隐藏/>30s 浮出实证·spinnerMeta.js 因经典 spinner 早已 bundled 故 require 即解析)。GUARDS 红线节(并入既有 spinner reveal-gate 红线一/三,记 ink TUI 同消费)。
  - [x] **刀15 ink TUI diff 内容列「裁切宽度」终端感知 + 展开换行走 SSOT(同 diff 域续刀:列宽算法·非行号位宽)** —— 对齐 CC `src/components/StructuredDiff/Fallback.tsx::formatDiff` **背后逻辑**:CC 按**终端宽度**算每行内容预算 `availableContentWidth = max(1, safeWidth - maxWidth - 1 - diffPrefixWidth)` 再 `wrapText(code, …, 'wrap')` → 长行**换行**铺开,**绝不丢内容**。真缺口=`tui/ink-components/ToolLines.js::renderDiffRows` 对 add/del/ctx/other 四类行 + `planWordDiffPairs` 全恒按**固定 100 字** `clip(text,100)` 硬截:既无视终端宽度(80 列 TTY 上 100 字反而被 ink 二次尴尬换行),又**`renderDiffRows` 不收 `expanded`**→展开(Ctrl+O)只抬**行**上限(`PREVIEW_ROWS`/`MAX_DIFF_ROWS`)而**不动横向**→列 100 之后的代码**展开后仍被静默吞掉**,直接违背本文件自述(注释 109-115)「ctrl+o to expand actually reveals the full change」诚实原则;且与 khy 自己经典 ANSI 路 `cli/diffRenderer.js`(无每行字数上限·console 自然换行)不一致。修=新纯叶子 `cli/diffContentWidth.js`:`diffClipWidth({columns,gutterWidth,expanded,env})` 纯算术(终端列宽由 ink call-site 注入·叶子不读 stdout)——门控关→恒 `LEGACY_CLIP`(100)逐字节回退;门控开**折叠**→ `max(20, columns - gutter - 3 - 3)` 单行预算(80 列→72·200 列→190 比 legacy 更宽);门控开**展开**→ `Infinity`(=不裁·`clip(s,Infinity)===s`·交 ink 像 CC 一样自动换行·内容完整)。接线=`renderDiffRows(rows,h,Box,Text,expanded=false)` 新增 `expanded` 形参,内部经 `diffClipWidth` 算 `clipW` 后把四处 `clip(r.text,100)` 换 `clip(r.text,clipW)`;`planWordDiffPairs(rows,wd,clipW=100)` 新增第三参(默认 100 保直接调用者/既有测试字节一致·`renderDiffRows` 传实时预算或 Infinity);两 call-site(`renderLiteralOutput` 的 shell-diff·`ToolLines` 的 write-diff)均把已有的 `expanded` 透传。**诚实边界(刻意不纳入)**=展开态长**词级配对**行(`del`紧跟`add`且改动比低)由 ink 对多段着色 `<Text>` 自动换行铺开·本叶子只产宽度数值不重排彩色段;经典 ANSI 路本就无每行上限不在收敛之列;ink@6.8.0 `output.js` trimEnd 裁尾随背景空格的 NA 不受影响(内容仍完整)。门控 `KHY_DIFF_CONTENT_WIDTH` 默认开。test:diffContentWidth(**node:test**)8/8(门控梯/关恒 100/开展开→Infinity/开折叠→`max(20,cols-gw-6)`/窄 TTY 触底 20/列宽未知→80 兜底/无参防呆/默认开读 process.env);toolLinesWordDiff(**jest**)新增 clipW 用例(默认 100·Infinity 全长不截·40 收紧)→ 16/16;既有「segments 同 100 宽界」用例不传 clipW 走默认 100 字节不变。三守卫过(leaf-contract diffContentWidth.js 净·model-hardcoding/agent-rules ToolLines.js 净)·TUI 305/305 无回归·toolLinesWriteDiff+toolLinesWordDiff 36→(36 基线 + 1 新)·bundled 三副本重建(各副本 diffContentWidth.js 解析 + 关=100/开展开=Infinity/开折叠=72 实证·ToolLines.js require/clipW/expanded 透传字符串校验通过)。GUARDS 红线节(diff 内容列裁切宽度只此一处 diffContentWidth·折叠按列宽·展开 Infinity 换行·门控关恒 100 字节回退)。
  - [x] **刀16 词级 diff「改动比例」度量口径从「非空白 token 计数」改「可见字符长度」走既有 SSOT(同 diff 域续刀:比例度量·非裁切宽度/行号位宽)** —— 对齐 CC `src/components/StructuredDiff/Fallback.tsx::generateWordDiffElements` **背后逻辑**:一行改得**太多**(`changeRatio > CHANGE_THRESHOLD` 0.4)就放弃词级高亮、整行回退;CC 用**可见字符长度**度量这个比例——`totalLength = removedLineText.length + addedLineText.length`(含空白与未改的公共文本)、`changedLength = Σ (added||removed) part.value.length`、`changeRatio = changedLength / totalLength`。真缺口=khy `cli/wordDiff.js::computeWordDiff`(diff 词级高亮的**唯一 SSOT 纯叶子**,被 `diffRenderer.js`/`ToolLines.js`/`ui/diffViewer.js` 三渲染器共用)历史却按**非空白 token 计数**度量(`changedNonWS/totalNonWS`,空白 token 既不进分子也不进分母),且自述注释本就声称「matching CC's CHANGE_THRESHOLD」=真·保真度 bug;两口径在阈值附近**翻转判定**:实证 `a = reallyLongOldName + b + c + d`→改名 17 字标识符,字符口径 34/66≈0.515 > 0.4(CC 判整行回退),token 口径 2/18≈0.111 ≤ 0.4(khy 误判词级)。修=**改既有叶子 SSOT 而非另起**:`computeWordDiff(oldTokens,newTokens,env)` 新增可选 env 形参,内部按 `wordDiffCharRatioEnabled(env)` 分支——门控开→字符口径(`tokenizeLine` 对每行**精确切分**故 `Σ token.length === line.length`,从 token 求和即逐字节复刻 CC 的 `line.length` 口径,**含前导缩进与未改公共文本进分母**);门控关→逐字节回退 legacy 非空白 token 计数。`renderWordDiffLine`/`computeWordDiffSegments` 同步透传 env(缺省 `process.env`),三消费者均不传 env → 默认开。**诚实边界(刻意不纳入)**=`CHANGE_THRESHOLD`(0.4)、`tokenizeLine` 切分规则、LCS/段合并(`_coalesceSegments`)均不动——本刀**只改比例的度量口径**这一处;不引入字符级 LCS(khy token 级切分本就够细,CJK 逐字成 token);着色/前缀留各 call-site。门控 `KHY_WORD_DIFF_CHAR_RATIO` 默认开。test:wordDiffSegments(**node:test**)11/11(新增刀16 字符 vs token 口径边界翻转:门控开→整行回退·门控关→词级只标改名段;门控关短词多改仍词级=字节回退验证;原 test5「仅缩进改动」注释纠正为「字符口径空白计入但缩进占比 6/24=0.25 ≤ 0.4 仍词级」);toolLinesWordDiff(**jest**)16/16(全经 computeWordDiffSegments 默认开·既有断言用小改动/纯改名→两口径同判无破坏)。三守卫过(leaf-contract/agent-rules/model-hardcoding wordDiff.js 净)·TUI 306/306 无回归·bundled 三副本重建(各副本 wordDiff.js 解析 + 门控开长标识符改名→solid·门控关→word-level·wordDiffCharRatioEnabled 导出实证)。GUARDS 红线节(词级 diff changeRatio 只此一处字符长度口径·绝不退回 token 计数·门控关逐字节回退)。
  - [x] **刀17 命令/三方应用 literal stdout 每行裁切宽度复用 `diffClipWidth` SSOT(同 diff 域续刀:补齐刀15 漏过的姊妹分支·非新逻辑)** —— 对齐 CC `src/utils/terminal.ts::renderTruncatedContent`(`src/components/shell/OutputLine.tsx` 消费)**背后逻辑**:命令 stdout 按**终端宽度**算每行预算 `wrapWidth = max(terminalWidth - PADDING_TO_PREVENT_OVERFLOW(10), 10)` 再 `wrapText('wrap')` → 长行换行铺开**绝不丢内容**。真缺口=`tui/ink-components/ToolLines.js::renderLiteralOutput` 的**非 diff 分支**(行 ~435)对每行恒按**固定 100 字** `truncate(ln, 100)` 硬截——与刀15 修掉的 diff 老缺口**同病**:① 无视终端宽度(80 列 TTY 上裁 100 仍宽于终端→ink 二次换行→撑破 `foldOutput` 的 maxLines 行预算·正是探查报告的「counting basis」病);② **展开(Ctrl+O)后仍裁 100**→列 100 之后的 stdout 静默吞掉·违背本文件自述「Ctrl+O 真正显示全貌」诚实原则。刀15 当时**只修了 diff 分支**(`renderDiffRows` 的 `clip`),literal 分支因走 `truncate`(而非 `clip`)被漏过。修=**复用既有 SSOT `diffContentWidth.diffClipWidth` 而非另起新叶子/新门控**(SSOT 纪律·同刀14 复用 spinnerMeta):`renderLiteralOutput` 非 diff 分支算 `litClipW = diffClipWidth({columns:(process.stdout.columns||undefined), gutterWidth:4, expanded, env})` 后把 `truncate(ln, 100)` 换 `truncate(ln, litClipW)`。`gutterWidth=4` = 外层工具块 marginLeft(1)+res 块 marginLeft(1)+行首 `⎿ ` 字宽(2) → 折叠 `cols-10` **逐字复刻 CC 的 `wrapWidth=cols-PADDING(10)`**;展开 → `Infinity`(`truncate(ln,Infinity)===ln` 整行全貌交 ink 自然换行);门控关(同 `KHY_DIFF_CONTENT_WIDTH`)→ 100 字节回退。**诚实边界(刻意不纳入)**=① `truncate` 的空白折叠语义(`replace(/\s+/g,' ')`)是另一轴不动(本刀只改宽度·空白保真属另一个可议的未来刀);② 行 617 是 `shouldRenderTransparentBody` **另一门控关**的 legacy 12 行预览字节回退,**不碰**(碰了破坏那个门控的字节契约);③ 行 554 错误行(`truncate(ln,120)`)是另一展示面不碰;④ `foldOutput` 的 head+tail 折叠设计(khy 刻意头尾窗口·与 CC head-only 不同但**各自正确**)**绝不**改成 CC 的 head-only(那会**删掉 khy 正确的尾部展示**=违诚实红线)——本刀只治每行**横向裁切宽度**,不动**纵向折叠策略**。门控 `KHY_DIFF_CONTENT_WIDTH`(复用刀15)默认开。test:inkRenderSmoke(**TUI harness**)新增刀17 两例(命令 stdout 折叠态 80 列→裁 70 行尾标记被裁·展开态 clipW=Infinity 整行全貌含尾标记·门控关 `KHY_DIFF_CONTENT_WIDTH=0` 折叠恒裁 100→86 字行全留字节回退)→ **TUI 308/308**(306 基线+2)。三守卫过(leaf-contract/agent-rules/model-hardcoding ToolLines.js 净·复用已 require 的 diffContentWidth 叶子无新依赖)·bundled 三副本重建(各副本 ToolLines.js 解析+`diffClipWidth({columns:80,gutterWidth:4})` 折叠=70/展开=Infinity/门控关=100 实证)。GUARDS 红线节(并入刀15 diffContentWidth 块:literal stdout 行同走 diffClipWidth·gutterWidth=4 复刻 CC cols-10·绝不再写固定 truncate(ln,100))。
  - [x] **刀18 工具「错误详情」多行折叠走新纯叶子 `cli/toolErrorFold.js`(同 fold 域续刀:错误展示面·补刀17 自述漏过的「行 554 错误行」)** —— 对齐 CC `src/components/FallbackToolUseErrorMessage.tsx` **背后逻辑**:错误折叠态 `error.split('\n').slice(0, MAX_RENDERED_LINES)`(10)行 + **独立 dim 页脚**「… +N lines (ctrl+o to see all)」(`plusLines = countCharInString(error,'\n') + 1 - MAX_RENDERED_LINES`),**展开(verbose/Ctrl+O)显示全部**——绝不静默丢尾。真缺口=`tui/ink-components/ToolLines.js` 错误分支(行 ~561)历史 `String(reason).split('\n').slice(0, 3)`:既**静默丢弃**超 3 行(无标记·用户看不出栈/构建/多行 stderr 错误下面还有内容)又**无视 expanded**(Ctrl+O 永不显全)——与刀17 修掉的 literal stdout「截断后 Ctrl+O 仍是空头支票」**同病**(刀17 自述「行 554 错误行属另一展示面」即此处)。**关键自查发现的隐藏前置 bug**=`errorText(result)` 经 `repl/displayFormatters.js::stripInternalControlText` 末尾 `.replace(/\s*\n\s*/g,' ')` **把所有换行折成空格** → 任何多行错误**早已被塌成一行**→`slice(0,3)` 在生产中**永不触发**→单改折叠**零可见变化**(=违诚实红线「绝不 ship 不改变可见输出的修复」)。故修=**两段式**(同一门控 `KHY_TOOL_ERROR_FOLD` 默认开):**①保留换行**——`stripInternalControlText(s, opts)` 新增**可选** `opts.preserveNewlines`(不传 → **逐字节回退**旧的「换行折空格→单行」·repl.js:7641 单行摘要消费者完全不受影响·实测 grep 确认 `errorText` 唯一消费者=ToolLines:563),`errorText(result, env)` 在门控开时传 `{preserveNewlines:true}`(逐行折空格+丢空行[含剥掉 `[SYSTEM:…]` 留下的空行]·保留行间结构),让多行错误按行铺开;**②折叠**——新纯叶子 `toolErrorFold.planErrorFold(lines, expanded, env)` 产 `{shown, hidden}`:门控开折叠态取前 `ERR_RENDERED_LINES`(10)+ honest `hidden` 计数·展开 → 全显;门控关 → **逐字节回退** legacy silent `slice(0,3)`(`hidden:0`·无视 expanded)。call-site 渲 `hidden>0` 时缀独立 dim 页脚「… +N 行 (ctrl+o 展开)」。**诚实边界(刻意不纳入)**=`_displayHint`(已干净单行·早 return 不经 strip)不动·`truncate(ln,120)` 单行宽度裁切(另一轴·刀17 的 literal 走 diffClipWidth 是另一分支)不动·`preserveNewlines` 绝不改 `stripInternalControlText` 默认行为(共享单行消费者字节契约)·CC 的 "Error: " 前缀属各 call-site 样式层(khy 用红色+headline 表意)不纳入。门控 `KHY_TOOL_ERROR_FOLD` 默认开。test:toolErrorFold(**node:test**)10/10(门控梯/`ERR_RENDERED_LINES===10`/折叠 ≤10 全显·==10 全显·13 行→头 10+hidden 3/展开 40 全显 hidden 0/门控关 silent slice(0,3) 无视 expanded·`LEGACY_ERR_LINES===3`/防呆非数组/发散仅在超 legacy cap 后);inkRenderSmoke(**TUI harness**)新增刀18 三例(13 行错误折叠→显 ERRLN-01/10·藏 ERRLN-13·页脚 `+3`/`ctrl+o`·展开 Ctrl+O→ERRLN-13 现身无页脚·门控关 `KHY_TOOL_ERROR_FOLD=0`→塌单行 ERRLN-13 内联无 ctrl+o 页脚字节回退)→ **TUI 318/318**。三守卫过(leaf-contract toolErrorFold.js+displayFormatters.js 净·agent-rules ToolLines.js 净·model-hardcoding 三文件净)·bundled 三副本重建(各副本 toolErrorFold.js 解析+`planErrorFold` 导出·displayFormatters `preserveNewlines` 保 3 行实证)。GUARDS 红线节。
  - [x] **刀19 任务清单面板「隐藏项摘要」从原始总数改 CC 式按状态分解走纯叶子 `taskPanelLines.summarizeHiddenTaskLines`(同 task-panel 域续刀:隐藏项语义·非高度封顶/图标映射)** —— 对齐 CC `src/components/TaskListV2.tsx::hiddenSummary` **背后逻辑**:任务清单超量被截断时,CC 在**隐藏(被截断)任务**上 `count(hiddenTasks, t=>t.status==='in_progress'/'pending'/'completed')` 按状态分类,按 **in progress → pending → completed** 次序拼成 `… +N in progress, M pending, K completed`(仅非零项出现)——让用户一眼看出「没显示的那些」里有几个在跑、几个待办、几个已完成。真缺口=`tui/ink-components/TaskListPanel.js` 尾切提示历史只给**原始总数** `⋯ 仅显示末尾 N 项（另有 M 项未显示）`,M 是个不透明计数,看不出隐藏的是进行中还是已完成(信息量远低于 CC)。**真实结构来源**:被 `liveRegionBudget.capTaskLines` 尾切丢弃的**头部行**每条行首恰是 `panelStatusIcon` 写入的状态图标(✓/→/○/✗),故分解来自图标逆映射 `ICON_STATUS`(**真实结构,非模糊启发式**——完全契合「注重背后的逻辑」)。修=**三处加性接线**(门控 `KHY_TASK_HIDDEN_BREAKDOWN` 默认开):① `liveRegionBudget.capTaskLines` **加性**返回 `hiddenLines`(被丢弃的头切片 `arr.slice(0, len-cap)`·无截断/非数组路径恒 `[]`·**不破坏**既有 `.lines`/`.hidden` 消费者,仅 test 两处 whole-object `deepEqual` 同步加 `hiddenLines:[]`);② `App.js` 把 `_capped.hiddenLines` 经 `_taskProps` 透传(Ctrl+T 隐藏 spread 同补 `hiddenLines:[]`);③ 新纯叶子函数 `summarizeHiddenTaskLines(hiddenLines, env)` 按 CC 次序(进行中→待办→已完成→错误)产「2 进行中, 1 待办, 1 已完成」,`TaskListPanel` 在其非空时渲 `⋯ 仅显示末尾 N 项（另有 <分解>）` 否则回退原始计数。**诚实边界(刻意不纳入)**=① 任一隐藏行行首**非已知图标**(如 V1 TodoWrite 折叠行 `- [ ]`)→`summarizeHiddenTaskLines` 返 ''→**回退原始总数,绝不少计**(宁可粗也不撒谎);② 门控关 / 空数组 / 非数组 → ''→ 逐字节回退原始计数标记;③ 标签用**中文**(Khy 中文作用域)但**计数+次序逻辑**逐项对齐 CC(非措辞照搬);④ CC 的 `strikethrough`(completed)/`bold`(in_progress)/blocked-by 显示/优先级截断排序属**别层**(本刀只治隐藏项摘要的分组口径,不动行内样式与截断策略——khy 尾锚定截断与 CC 优先级截断各自正确)。门控 `KHY_TASK_HIDDEN_BREAKDOWN` 默认开。test:taskPanelLines+liveRegionBudget(**node:test**)32/32(新增刀19:多状态 CC 次序/仅非零项/不可识别图标→''/空/门控关→''/trimStart 容前导空白/`taskHiddenBreakdownEnabled` 门控梯·capTaskLines 加 `hiddenLines` 头切片+空回退);taskListPanel(**TUI harness**)新增刀19 三例(hiddenLines 带图标→分解标记 `2 进行中, 1 待办, 1 已完成`·不可识别行→回退原始总数·门控关→回退原始总数)→ **TUI 320/320**。三守卫过(leaf-contract taskPanelLines.js+liveRegionBudget.js 净·agent-rules/model-hardcoding 四文件净·App.js 1 warning 预存)·bundled 三副本重建(各副本 `summarizeHiddenTaskLines`/`taskHiddenBreakdownEnabled` 导出+`capTaskLines` 含 `hiddenLines`+门控开分解/关回退实证)。GUARDS 红线节。
  - [x] **刀20 agent 树「时长 elapsed」控制器路径走时长 SSOT `agentStatLine.agentDurationLabelOr`(同 agent 树域续刀:时长格式·两前端一致性·非统计行/liveness)** —— 对齐 CC `src/utils/format.ts::formatDuration` **背后逻辑**:agent 树有两个前端,ink TUI 走**事件溯源 number elapsed**,经 `agentTreeView.formatStats` 的 number 分支 → `agentDurationLabelOr` → 门控开 `ccFormatDuration`("1m 30s"/"2s"),早已对齐 CC;但非 TTY/经典路径走 `cli/agentTreeController.js::toAgentArray()`,历史把 elapsed **预格式化成恒 `(ms/1000).toFixed(1)+'s'`**(`formatStats` 字符串分支原样 verbatim push),是两前端中**唯一绕过时长 SSOT** 的一支 → **同一个 agent 在 ink TUI 显 "1m 30s"、在非 TTY 控制器路径却显 "90.0s"**(≥60s 不进位、亚秒 "2.1s" vs CC 整秒 "2s")。**真缺口**=控制器是不一致的孤儿(**非「故意保留亚秒精度」**——number 分支门控开本就已下取整到整秒,控制器只是没跟上)。修=**SSOT 纪律,消费既有叶子而非另起**:`agentTreeController` `require('./agentStatLine')`,`toAgentArray` 的 elapsed 改 `elapsed > 0 ? agentDurationLabelOr(elapsed, \`${(elapsed/1000).toFixed(1)}s\`, process.env) : ''` ——**保持 elapsed 仍是 STRING**(agent-array 形状的全部消费者 `formatStats` 字符串分支 / `toolDisplay.js:639` / `panels.js:661` 都按 string 原样 push·`panels` docstring 明示 `@param {string} elapsed`),只把它的**值**改由 SSOT 产:门控开(`KHY_CC_FORMAT` 默认开)→ CC 形态("1m 30s"/"2s"),门控关 → legacy `X.Xs`(`90.0s`/`2.1s`)逐字节回退。**诚实边界(刻意不纳入)**=① `elapsed` 类型**保持 string 不可改 number**(否则 verbatim-push 消费者全断);② `agentRenderer.js:140` 的 `Math.max(...map(a=>a.elapsed||0))` 对字符串本就 NaN——是**预存**的非 TTY summary 路径独立 bug(与 elapsed 是 "90.0s" 还是 "1m 30s" 无关,两者皆 NaN),本刀**不引入也不修**(非本刀职责,改它属另一刀);③ ink TUI 事件溯源 number 路径早已走 SSOT,本刀**只补控制器这一支非 TTY/经典路径**使两前端一致。门控 `KHY_CC_FORMAT`(复用 ccFormat 家族总开关)默认开。test:agentTreeController(**jest**)7/7(门控开 ≥60s→"1m 30s"/亚秒→"2s"·门控关 byte-identical "90.0s"/"2.1s"·elapsed 0→''·与 `agentDurationLabelOr` 输出逐项一致[500/2100/59000/90000/3725000 两门控态]·error 态同走 SSOT·类型恒 string);agentTreeView(jest)35/35 + agentStatLine(node:test)13/13 无回归。三守卫过(leaf-contract/agent-rules/model-hardcoding agentTreeController.js 净)·TUI 320/320 无回归(ink 事件溯源 number 路径不受影响)·bundled 三副本重建(各副本 `agentTreeController.js` 解析+`require('./agentStatLine')`+门控开 90000→"1m 30s"/门控关→"90.0s" 实证)。GUARDS 红线节。
  - [x] **刀21 spinner / step 行 / task 面板「秒级时长 elapsed」走时长 SSOT `renderTheme._formatElapsed → ccFormatDuration`(时长格式器域续刀:补刀20 之后**最后一支**绕过 ccFormat 的孤儿)** —— 对齐 CC `src/utils/format.ts::formatDuration` **背后逻辑**:`renderTheme._formatElapsed(totalSec)` 是 spinner(`spinner.js:140` elapsedSec)、step 行(`steps.js:68` secs)、task 面板(`taskMindMap.js:312/367/384/387` `(Date.now()-createdAt)/1000`)三处秒级耗时展示的**唯一格式器**(经 `aiRenderer.js:29`/`panels.js:16`/`toolDisplay.js:20` 再导出)。历史实现**本地手算**:`<60s` 显 `${sec}s`,否则 `${min}m`(整分**丢秒**显 "2m" 而非 "2m 0s")、且**永不进位小时**(分钟无界增长 "123m 4s" 而非 "2h 3m 4s")——是时长格式器家族(HUD `{hideTrailingZeros}` / turnStats `humanizeElapsed` / CompactionProgress `formatElapsed` / AgentTool `_fmtElapsed` / agent-tree 刀20 `agentDurationLabelOr`)中**最后一支**绕过 `ccFormat` SSOT 的孤儿。**真缺口**=同一耗时在 HUD/agent 树显 CC 形态、在 spinner/task 面板却显本地异口径(≥1m 丢秒、≥1h 不进位)。修=**SSOT 纪律,消费既有叶子而非另起**:`renderTheme` `require('./ccFormat')` 取 `ccFormatEnabled/ccFormatDuration`,`_formatElapsed` 门控开 → `ccFormatDuration(Math.max(0,Math.floor(totalSec))*1000)`(保整分 `0s`、滚动进 h/d,对齐 CC spinner `SpinnerAnimationRow` 调 `formatDuration()` **无 options**=PLAIN 口径,与 CompactionProgress/turnStats/agentStatLine 同约定,**非** HUD 的 `{hideTrailingZeros:true}`),门控关(`KHY_CC_FORMAT=0`)→ 本地手算逐字节回退(`${min}m`/`${min}m ${rem}s`)。**诚实边界(刻意不纳入)**=① 亚秒(`<60s`)**两态字节一致**(`ccFormatDuration` 的 `${Math.floor(n/1000)}s` 与 legacy `${sec}s` 同),发散**只在**整分 `0s`(`"1m 0s"` vs `"1m"`)与 ≥1h 进位(`"1h 2m 3s"` vs `"62m 3s"`);② 全部调用方均传**秒**(已核 spinner/steps/taskMindMap·`Math.max(0,…)` 守负值)不混毫秒;③ HUD 的 `{hideTrailingZeros:true}` 是**另一约定**(`60s→"1m"` 省尾零)不动·本刀只统一 spinner/step/task 这条 PLAIN 链。门控 `KHY_CC_FORMAT`(复用 ccFormat 家族总开关)默认开。test:renderThemeFormatElapsed(**node:test**)6/6(门控开整分保 `0s`/≥1h 进位/亚秒 byte-identical/与 `ccFormatDuration(sec*1000)` 逐项一致/门控关回退本地口径/唯一发散点=整分与 ≥1h)+ spinnerMeta+hudDurationSsot(node:test)13/13 无回归 + taskMindMap(jest)无回归。三守卫过(leaf-contract/agent-rules/model-hardcoding renderTheme.js 净)·TUI 320/320 无回归·bundled 三副本重建(各副本 `renderTheme.js` 解析+`require('./ccFormat')`+门控开 60s→"1m 0s"/3723s→"1h 2m 3s"/门控关→"1m"/"60m" 实证)。GUARDS 红线节。
  - [x] **刀22 工具头 `name(arg-summary)` 的 Bash `command` 长度上限从统一魔法数 60 改对齐 CC `MAX_COMMAND_DISPLAY_CHARS=160`(新纯叶子 `cli/toolHeaderCap.js` 收敛 per-key 上限·非措辞/路径/折叠)** —— 对齐 CC **按工具**头展示 `packages/builtin-tools/src/tools/BashTool/UI.tsx::renderToolUseMessage`。**背后逻辑**:CC 每个工具走自己的 `renderToolUseMessage`,Bash 命令头按 `MAX_COMMAND_DISPLAY_CHARS=160`(另留 `MAX_COMMAND_DISPLAY_LINES=2` 多行头)、grep pattern 按 `TOOL_SUMMARY_MAX_LENGTH=50`(`src/constants/toolLimits.ts`)——都是**经过考量的按工具上限**。Khy `tui/ink-components/ToolLines.js::summarizeArgs` 对**每个**描述键(command/file_path/path/pattern/query/url/prompt/description/泛型 k=v)一律 `truncate(...,60)`=**统一魔法数**(无注释标其为刻意宽度)。**真缺口**=多数 key 的 60 ≥ CC per-tool 上限(grep pattern 60>50,Khy 显得**更多非更差**)故无须动;唯独 Bash `command` 的 60 远低于 CC 的 160,而 **Ink TUI 里命令只出现在这条头行**(`summarizeArgs(t)`→`name(argSummary)` 行 526/546·`SHELL_*`/`isShellResult` 机制全是 RESULT stdout 折叠不含命令本身·**不像经典 REPL** `toolDisplay.js` 另有命令模式识别 + 8 行 box 显全貌)→ 61–160 字 Bash 命令被截断在 60 且**无处可寻**=明显比 CC 差(实证 66 字 `git log --oneline --graph --all --decorate --abbrev-commit -n 50`:CC 显全·Khy 截成 `…abbrev-comm…`)。**修(SSOT 纪律)**=新纯叶子 `cli/toolHeaderCap.js`:`MAX_COMMAND_DISPLAY_CHARS=160`(CC 常量)+`LEGACY_ARG_CAP=60`+`argDisplayCap(key,env)`(门控开 → command/cmd→160、其余 key→60;门控关 → 全 60 字节回退);`summarizeArgs` 非路径描述键 `truncate(String(obj[key]), argDisplayCap(key, process.env))`。**诚实边界(刻意不纳入)**=① **只对齐字符上限**,不引入 CC `MAX_COMMAND_DISPLAY_LINES=2` 多行命令头(Khy 刻意 `replace(/\s+/g,' ')` 单行折叠·**刀17 已裁定 NA**);② 非 command key 的 60 **不降到 CC 的 50**(Khy 显得更多非更差·降级=删正确行为违诚实红线);③ 路径键 `file_path`/`path` 仍走 `truncatePathMiddle`(另一轴)不动;④ 经典 REPL `toolDisplay.js` 命令头是**刻意不同**的富设计(命令模式识别「git log …」+8 行 box 显全貌·刀17 NA)非本刀对象——故**非两 call-site 收敛**·本刀仅治 TUI 这一支。门控 `KHY_TOOL_HEADER_CAP` 默认开。test:toolHeaderCap(**node:test**)5/5(常量 160/60·门控开 command/cmd→160 其余→60·门控关全 60·门控梯大小写空白·唯一发散点仅 command/cmd 两态不同其余两态恒 60)+ summarizeArgsPathTruncate(**jest**)更新为 6/6(原「command 80 字截 60」改「command 200 字端截 160」保 END-truncate 意图+新增「66 字命令门控开显全/关截断 60」)。三守卫过(leaf-contract/agent-rules/model-hardcoding toolHeaderCap.js+ToolLines.js 净)·TUI 320→**321/321**(+1=path-truncate 套件新增一例)·bundled 三副本重建(各副本 `toolHeaderCap.js` 解析+`argDisplayCap` 导出+`summarizeArgs` 门控开 66 字命令显全 cap=160/门控关截断 cap=60 实证)。GUARDS 红线节。
  - [x] **刀23 常驻任务面板「已完成(✓)行 strikethrough」对齐 CC `TaskListV2.strikethrough={isCompleted}`(新纯叶子 `cli/tui/ink-components/taskLineStyle.js` 收敛行样式·非措辞/计数/折叠)** —— 对齐 CC `src/components/TaskListV2.tsx::TaskItem`。**背后逻辑**:CC 渲染 `<Text bold={isInProgress} strikethrough={isCompleted} dimColor={isCompleted||isBlocked}>{displaySubject}</Text>`,对**已完成**任务标题做 `strikethrough`(划掉),长清单里「已做完的事」一眼可辨被划掉而不只是变暗;图标在**另一** `<Text color>` 节点(故 CC 只划 subject 不划图标)。Khy `TaskListPanel._iconStyle`(行 78 `h(Text,{..._iconStyle(line)},line)`)对 ✓ 行只给 `{color:'green',dimColor:true}`,**从不加 strikethrough**——已完成项仅暗绿,扫读时与待办难区分。行首图标(✓/→/✗/○)来自 `_taskStore.snapshot()`(`_taskStore.js:303` ✓)/`taskPanelState.getTasks()` 的**结构化状态**(非模糊推断),确定性可划。**真缺口**=同一已完成任务在 CC 被划掉、在 Khy 只暗绿 = 明显比 CC 差;且面板挂 App live 区(`App.js:1945` `nowTick` 1s 重渲染)默认开(`KHY_TASK_PANEL` 默认 on)= **可达**;CC 侧 `isTodoV2Enabled()`(`tasks.ts:133`)交互会话恒 true = 默认 REPL 即渲染。**修(SSOT 纪律)**=抽纯叶子 `taskLineStyle.js`:`taskStrikethroughEnabled(env)` + `taskLineStyle(line,env)`(与历史 `_iconStyle` 同源,门控开时 ✓ 行附加 `strikethrough:true`,门控关与历史返回对象逐字节一致),`TaskListPanel` `require('./taskLineStyle')` 消费、删内联 `_iconStyle`、行 78 改 `...taskLineStyle(line, process.env)`。**诚实边界(刻意不纳入)**=① Khy 面板每行是**单个 Text 节点**(图标+文本同节点),故 strikethrough 作用于**整行**(含 ✓ 图标),非如 CC 仅划 subject——渲染结构决定的**刻意简化**,非缺陷;拆「图标 Text+文本 Text」会改布局且超范围(**正是刀19 把 strikethrough 列「暂缓」的顾虑**·刀23 改用整行划线**绕开拆节点风险**而非真去拆);② 仅 completed(✓)加划线对齐 `strikethrough={isCompleted}`;in_progress(→)bold、pending(○)dim、error(✗)red 等其余样式**不动**(Khy cyan/green/red 着色是相对 CC 的增强保留不删);③ CC 对 pending **不** dim、对 blocked 才 dim,Khy 对 pending dim 是可辩护的「弱化未开始项」设计**非明显更差不动**(改之即删既有刻意选择违诚实红线)。门控 `KHY_TASK_STRIKETHROUGH` 默认开。test:taskLineStyle(**node:test**)6/6(门控开 ✓=green+dim+strikethrough·其余状态不变·门控关 ✓ 回退仅 green+dim 无 strikethrough·门控梯大小写空白·唯一发散点仅 ✓ 两态不同其余恒等·前导空白容忍)+ taskListPanel(**ink-tui runner**)更新(完成行新增 `strikethrough===true` 断言 + 新增门控关 `strikethrough===undefined` 回退例)。三守卫过(leaf-contract/agent-rules/model-hardcoding taskLineStyle.js+TaskListPanel.js 净)·TUI 321→**322/322**(+1=taskListPanel 套件新增门控关一例)·bundled 三副本重建(各副本 `taskLineStyle.js` 解析+两导出+门控开 ✓.strike=true/门控关 undefined + `TaskListPanel` wired=yes 实证)。GUARDS 红线节。
  - [x] **刀24 TUI 斜杠命令菜单「过滤/排序」从仅前缀升级到子串/分段/描述匹配,消费既有 `rankSlashCommands` SSOT(新领域:TUI 补全菜单过滤·非措辞/计数/折叠/样式)** —— 对齐 CC `src/utils/suggestions/commandSuggestions.ts::generateCommandSuggestions`。**背后逻辑**:CC 用 Fuse 索引,键含命令名(w3)、**`partKey`=命令名按 `[:_-]` 切分**(w2)、别名(w2)、描述(w0.5),再按 exact>prefix>fuzzy 排序——即**子串/分段/描述**都能命中,不止前缀。**真缺口**=Khy **TUI** 菜单(`tui/hooks/useCompletions.js::computeSlash`)历史走 `router.getCompletions`→`commandRegistry.getCompletions` 的**仅前缀** `startsWith`+字母序 `.sort()`;对 Khy 的连字符命令(`/autofix-pr`/`/commit-push-pr`/`/subscribe-pr`),键 `/pr` 在 TUI **完全搜不到**(菜单甚至整个消失)——而 **Khy 自家经典 REPL** 早用 `repl/slashCommandFilter.js::rankSlashCommands` 做「前缀>命令子串>标签/描述子串」匹配(`/pr` 经子串命中 `/autofix-pr`)→ **TUI 退化到比 Khy 自家 CLI 还差**,也比 CC 差。**可达**=`computeSlash` 是 TUI 输入区每帧补全的唯一斜杠源(`KHY_FULL_TUI` 默认开)。**修(SSOT 纪律,消费既有叶子而非另起算法)**=抽纯叶子 `cli/tui/slashMenuFilter.js`:`slashMenuCommandNames(value,{slashCommands,getCompletionsFn},env)` 门控开时把 TUI 菜单路由到 `rankSlashCommands`(同一排序内核,与经典 REPL 收敛),门控关时**注入式**回退到原 `getCompletionsFn(value)`(仅前缀);`useCompletions.computeSlash` 改 `r.SLASH_COMMANDS`+`(v)=>r.getCompletions(v)` 注入消费,菜单项 label/desc 仍由既有 `slashDescription` 装配(渲染外观对同一命令完全一致,唯一发散=菜单命令**集合与顺序**)。**诚实边界(刻意不纳入)**=① **绝不碰** `commandRegistry.getCompletions` 本身——它另喂经典 readline `completer`(`repl.js:846`),readline 靠「候选共同前缀」补全,**前缀语义在那里是对的**,换子串会破坏 readline 补全;本刀**只治 TUI 菜单这一支**;② 排序/分词/打分**全部委托** `rankSlashCommands`(prefix=3/cmd-substring=2/label|desc-substring=1·稳定排序),本叶子不自造评分(否则重复 SSOT);③ 只产**命令名有序列表**。**进一步扫候选 honest-NA**:权限确认框选项过滤(`PermissionsPrompt` 是固定 allow/deny 枚举非搜索·无过滤逻辑可对齐)·`@`-mention 文件补全(`computeFile` 已是 `startsWith` 路径前缀·CC 文件补全亦前缀+fs 序·无发散)·thinking block 折叠展示(Khy 无独立 thinking 块渲染面=结构缺失·造则 net-new 非对齐)。门控 `KHY_TUI_SLASH_SUBSTRING` 默认开。test:slashMenuFilter(**node:test**)7/7(门控开 `/pr` 子串命中连字符命令+前缀分数在前·门控关逐字节回退前缀路径连字符命令消失·`/push`→`/commit-push-pr`·描述/标签子串命中·门控梯·防呆空·`/mo` 前缀按原始下标序)+真路由 e2e(`router.SLASH_COMMANDS` 146 命令:门控开 `/pr` 浮出 `/commit-push-pr`+`/autofix-pr`+`/subscribe-pr`·门控关空且 `=== router.getCompletions("/pr")`)。三守卫过(leaf-contract/agent-rules/model-hardcoding slashMenuFilter.js+useCompletions.js 净)·TUI **322/322** 无回归(useCompletions 无专属 TUI 套件,菜单形状未变)·bundled 三副本重建(各副本 slashMenuFilter.js 解析+两导出+门控开浮出连字符命令/门控关回退 + useCompletions wired=yes 实证)。GUARDS 红线节。
  - [x] **刀26 工具失败串「受众拆分」:给「人」显示的入参校验失败串折成单行 `Invalid tool parameters`,完整分组细节只给「模型」+ Ctrl+O 展开(新纯叶子 `cli/ccUserFacingToolError.js` 消费产串方自识别签名 `ccValidationError.isValidationErrorMessage`·与刀「ccValidationError 给模型分组措辞」互补正交·非措辞/折叠口径/计数)** —— 对齐 CC `src/components/FallbackToolUseErrorMessage.tsx`。**背后逻辑**:CC 把工具失败串**分两个受众**——给**模型**的 tool_result 保留 `formatZodValidationError` 的**完整分组串**(模型自我纠正的唯一依据),给**人**的默认(非 verbose)视图 `if (!verbose && trimmed.includes('InputValidationError: ')) error = 'Invalid tool parameters'` 折成**一行**,完整细节仅经全局 Ctrl+O(transcript)展开。**真缺口**=Khy 在**默认 TUI** 渲染器(`tui/ink-components/ToolLines.js` 的 isErr 分支 ~line 580)把那条**面向模型**的多行分组校验串(`${tool} failed due to the following issues:\n…`)**原样**铺给人——它落在刀18 折叠阈值(10 行)之内**不被折叠**,故同一份串既发模型又显给人,从不像 CC 那样**按受众拆分**(而 Khy 自家 `ccValidationError` docstring 早写明那串是「返回给**模型**…模型自我纠正的唯一依据」)。**可达**=默认 TUI(`KHY_FULL_TUI` 默认开)工具失败行。**修(SSOT 纪律)**=① 产串方 `tools/ccValidationError.js` 新增**自识别**判据 `isValidationErrorMessage(text)`(只认自己产的 `Validation failed: …` 与 `… failed due to the following issue(s):` 两形;**签名由产串方 SSOT 拥有**,与 CC 凭 `includes('InputValidationError: ')` 识别自家校验错误**同一原理**·非模糊启发式);② 新纯叶子 `cli/ccUserFacingToolError.js::collapseValidationErrorForDisplay(text,{expanded},env)`——门控开 + 折叠态 + 是校验串 → `Invalid tool parameters`(`COLLAPSED_VALIDATION_TEXT`);展开(Ctrl+O)/非校验串/门控关/非串 → **原样**;③ `ToolLines.js` isErr 分支把 `errorText(t.result)` 产物先过此叶子(传 `{ expanded }`)再交既有 `planErrorFold`(刀18 折叠)。**诚实边界(刻意不纳入)**=① **只折叠本仓自产校验串**;**非校验类**失败(权限被拒绝、bash 退码、网络错误等)**逐字节原样透传**(对人有信息量·CC 也只折校验类);② **可恢复**:折叠后完整细节仍可经既有全局 Ctrl+O(`expanded`)看到(=CC 的 transcript 拆分)·不丢信息;③ **模型侧 tool_result 不动**(本刀只在 `ToolLines` display 层调用·不碰 `tools/index.js` 的 `result.error`);④ **不接经典 readline REPL**(`repl.js:7636` 那支已 `.slice(0,120)` 把多行折成单行截断=不同的既有行为·且其 isErr 分支无 `_expandableOutputs` 注册→折叠会无 Ctrl+O 恢复=不诚实·故诚实留 honest-NA);⑤ **不引入** CC 的 `Error:` 前缀归一(改动面遍及所有工具错误、Khy 错误本地化措辞各有设计·非清晰缺口·刻意不做)。门控 `KHY_USER_FACING_TOOL_ERROR` 默认开·关/`expanded` → 原样返回逐字节回退(显完整分组)。test:ccUserFacingToolError(**node:test**)16/16(产串方自识别签名 4 例·门控梯·折叠态校验串→单行·legacy 也折·展开→完整保 `todos`·非校验类原样·门控关字节回退·默认开·防呆非串/opts 缺省·开关唯一分歧=校验串)+ ccValidationError(node:test)10/10 + builtinSchemaValidation(jest)4/4 无回归。三守卫过(leaf-contract ccUserFacingToolError.js+ccValidationError.js 净·agent-rules/model-hardcoding ToolLines.js 净)·TUI **322/322** 无回归·端到端 pipeline 探针(模型 full 不变 / 门控开折叠→单行 / 展开→full / 门控关→字节回退)·bundled 三副本重建(4 文件×3=12 byte-identical + 各副本 runtime `on="Invalid tool parameters" off==full`)。GUARDS 红线节。
  - [x] **刀27 Read 工具头行路径后追加「读取范围」后缀(`offset`/`limit` → `第 X-Y 行` / `从第 X 行起`),新纯叶子 `cli/readRangeSuffix.js` 收敛两处头渲染,绝不再把 read 真实携带并消费的 offset/limit 整段丢弃只回显裸路径(新领域:工具头「读取范围」算术·非措辞/计数/折叠/样式)** —— 对齐 CC `packages/builtin-tools/src/tools/FileReadTool/UI.tsx::renderToolUseMessage`。**背后逻辑**:CC 在 Read 头行路径之后用一段具体算术折出行区间:`const startLine = offset ?? 1; const lineRange = limit ? \`lines ${startLine}-${startLine + limit - 1}\` : \`from line ${startLine}\``,即 `Read(foo.ts · lines 40-80)`(offset+limit)/ `Read(foo.ts · from line 40)`(仅 offset)。**真缺口**=Khy 两处工具头渲染——经典 REPL `toolDisplay.js:424` read 分支 `return params.file_path||…`、默认 TUI `ToolLines.js::summarizeArgs` 命中 file_path/path 即返截断路径——**都只回显裸路径**,把 `src/tools/readFile.js` inputSchema 真实声明(offset/limit)并在第 105-109 行据此切行区间消费的数据**整段丢弃**:模型分页读大文件某一段时,用户看不出读了哪一段。**可达**=大文件分页读(readFile 自带 offset/limit pagination hint,模型常用)。**修(SSOT 纪律)**=新纯叶子 `cli/readRangeSuffix.js::buildReadRangeSuffix(params,env)` 移植 CC 那段算术(`startLine + limit - 1`、无 limit 时 `从第 N 行起` 回退;只认**正整数** offset/limit,数字串也接受);两处头渲染在路径串之后 `+ buildReadRangeSuffix(...)`(TUI 侧再以 `isReadToolName(tool.name||toolName||tool)` 门控只对 read 生效,write/edit/grep 不追加)。**诚实边界(刻意不纳入)**=① **绝不移植 CC 的 `pages` 分支**(Khy read **无** `pages` 参数,凭空造 `· pages N` = 臆造 Khy 不携带的数据→违诚实红线);② offset/limit **无条件**展示(不做 CC 的 verbose 门控——Khy 无 verbose 概念,且行区间是「读了哪一段」的实质信息而非冗余,大多数读不设 offset/limit 故不嘈杂·与 CC 自身**总是**展示 `pages` 分支同取向,只落到 Khy 携带的行区间数据上);③ 只认正整数(0/负/小数 floor/非数/NaN/Infinity 忽略·非对象参数→空后缀不抛)。门控 `KHY_READ_RANGE_SUFFIX` 默认开·关 → 返回 `''` 头行只剩裸路径逐字节回退。test:readRangeSuffix(**node:test**)16/16(门控梯·`isReadToolName` 归一·offset+limit→`第 X-Y 行`·仅 limit→start 回退 1·仅 offset→`从第 N 行起`·非正整数忽略·数字串接受·绝不产 pages·门控关字节回退·防呆非对象)无回归。三守卫过(leaf-contract/agent-rules/model-hardcoding readRangeSuffix.js 净)·TUI **322/322** 无回归(既有 summarizeArgs 测试用非 read 工具或无 offset/limit→后缀恒空字节不变)·端到端 TUI 渲染探针(`read+offset+limit`→`src/x.js · 第 40-80 行`·`write+offset+limit`→裸路径无后缀·`read 无范围`→裸路径·门控关→字节回退裸路径)·bundled 三副本重建(3 文件×canonical=byte-identical + 各副本 runtime `read+range="… · 第 40-80 行" write==裸路径`)。GUARDS 红线节。
  - [x] **刀28 常驻任务清单面板头行从静态 `任务清单` 升级为「按状态计数」标题(total + 完成 + 进行中(仅 >0) + 待办 + 错误(仅 >0)),新纯叶子 `cli/tui/ink-components/taskPanelHeader.js` 消费 `taskPanelLines.countTaskLinesByStatus`(图标→状态逆映射 SSOT)·计数覆盖全量(可见 + 隐藏)(新领域:任务面板头行计数·非措辞/折叠/样式)** —— 对齐 CC `src/components/TaskListV2.tsx` 的 `isStandalone` 头行。**背后逻辑**:CC 标准任务清单(`<TaskListV2 isStandalone>`,挂 `REPL.tsx`)算 `completedCount`/`pendingCount`/`inProgressCount = tasks.length - completed - pending`,头行 `{tasks.length} tasks ({completedCount} done, [{inProgressCount} in progress, ]{pendingCount} open)`——total 与 done/open **总在**,in-progress **仅 `inProgressCount > 0`** 才出现一段。**真缺口**=Khy `TaskListPanel.js:68` 头行只渲染静态 `h(Text,{dimColor:true},'任务清单')`,**没有任何 total/完成/进行中/待办分解**——用户看不出清单整体进度(几项、几项已完成、几项在跑)。**可达**=默认 TUI(`KHY_FULL_TUI` 默认开)输入框上方常驻任务面板,coordinated(App 传 `lines`+`hidden`+`hiddenLines`)与 standalone(自读 `_taskStore`+`taskPanelState`)两模式都经此头行。**修(SSOT 纪律)**=① `taskPanelLines.js` 新增 `countTaskLinesByStatus(lines)`——复用既有 module-private `ICON_STATUS`(✓完成/→进行中/✗错误/○待办)逆映射,按行首图标计数,未知图标计入 `unknown`(**不另立第二份图标表**);② 新纯叶子 `cli/tui/ink-components/taskPanelHeader.js::buildTaskPanelHeader({lines,hidden,hiddenLines},env)` 移植 CC 计数(完成/待办总在·进行中仅 >0·**khy 诚实扩展 error(✗)仅 >0**——CC 把非 completed/pending 一律并进 in-progress,khy 有显式 error 状态故单列更诚实);③ `TaskListPanel.js:68` 用 `buildTaskPanelHeader(...) || '任务清单'` 装配头行。**诚实边界(刻意不纳入)**=① 计数源是**已渲染行的行首图标**(`panelStatusIcon` 写入的真实结构),不是模糊启发式;② **计数必须覆盖全量**(可见 `lines` + 全部 `hiddenLines`)——`hidden>0` 却拿不到等长 `hiddenLines`(无法逐行归类)、或任一行行首非已知图标(`unknown>0`)→ `buildTaskPanelHeader` 返 `''` → 回退静态 `任务清单`,**绝不少计/错计/把未知行静默并进某一类**;③ 与刀19 隐藏摘要行(`⋯ 仅显示末尾 N 项(另有 …)`)**互补不重复**:头行给**全量按状态总计**,刀19 行给「可见尾切 vs 隐藏」的展示告知;④ 进行中/错误仅 >0 出现(对齐 CC `inProgressCount>0`)。门控 `KHY_TASK_PANEL_HEADER` 默认开·关 → `buildTaskPanelHeader` 返 `''` 头行回退静态 `任务清单`逐字节回退。test:taskPanelHeader(**node:test**)18/18(门控梯·完成待办总在/进行中仅 >0·error 仅 >0·全完成·coordinated 全量计数·hidden 数不符回退·hidden=0 仅可见·未知图标回退·隐藏行未知图标回退·空清单·门控关回退·防呆非对象·total===lines+hidden)无回归。三守卫过(leaf-contract/agent-rules/model-hardcoding taskPanelHeader.js+taskPanelLines.js 净)·TUI 322→**323/323**(+1=taskListPanel 套件新增门控关 `KHY_TASK_PANEL_HEADER=0` 回退静态 `任务清单` 一例;另更新 3 例既有断言从静态标签改为计数头行)·端到端探针(门控开头行带计数·门控关 `''`·coordinated 可见+隐藏全量计数)·bundled 三副本重建(3 文件×canonical=byte-identical + 各副本 runtime `on="任务清单(共 4 项:…)" off="" hid=可见+隐藏全量计数`)。GUARDS 红线节。
  - [x] **刀29 任务快照行「blocked by」后缀只列**仍未完成**的依赖,已完成的依赖剔除、全部完成则后缀整段消失,新纯叶子 `tools/taskBlockedBySuffix.js` 收敛 `_taskStore.snapshot()` 那段后缀构造,绝不再把原始 `blockedBy.join(',')` 原样铺出(新领域:任务行依赖注解的「过期阻塞过滤」·非措辞/计数/折叠/样式)** —— 对齐 CC `src/components/TaskListV2.tsx` 的 `openBlockers`。**背后逻辑**:CC 算 `unresolvedTaskIds = new Set(tasks.filter(t => t.status !== 'completed').map(t => t.id))`,`openBlockers = task.blockedBy.filter(id => unresolvedTaskIds.has(id))`,渲染 `isBlocked && (… blocked by {[...openBlockers].sort((a,b)=>parseInt(a)-parseInt(b)).map(id=>'#'+id).join(', ')})`——**依赖一旦完成就从「blocked by」注解剔除**,全部完成则 `isBlocked=false` 整段消失,展示的永远是**当前仍在阻塞**的依赖;survivors 数字序 + `#` 前缀。**真缺口**=Khy `_taskStore.snapshot():304` 把原始 `blockedBy` 数组 `join(',')` **原样**铺出(`[blocked by: 1,3]`),依赖完成后仍把已完成的阻塞显示出来 → 过期、误导(任务其实已可启动却仍标着「blocked by」)。注意 khy 自家 `canStart():93` 早有同款完成判据 `_legacyStatus(dep.status) !== 'completed'`,故快照层显示与 claim 层判定**自相矛盾**=真缺陷非刻意。**可达**=快照消费者:压缩管线(`compactPipeline`/`contextCompressor` 喂模型上下文)与常驻 TUI 任务面板 standalone 模式(`TaskListPanel` 经 `mergeTaskLines`)。**修(SSOT 纪律)**=① 新纯叶子 `tools/taskBlockedBySuffix.js::buildBlockedBySuffix(blockedBy, completedIds, env)` 过滤 `completedIds` 命中者、`#` 前缀 + 数字序(非数字 id 退稳定字典序,khy id 可为 `t-xxx` 形);② `snapshot()` 用既有 `list()` 一次性建 `completedIds = new Set(tasks.filter(status==='completed').map(t=>String(t.id)))`,逐行调叶子替换内联三元。**诚实边界(刻意不纳入)**=① 只剔除**确已完成**(`completedIds` 命中)的依赖;**缺失/不存在**的依赖 id **保留显示**(与 khy 自家 `canStart():92` 的「missing dep = blocked」语义一致、对悬空依赖是有用告警)——这是相对 CC `unresolvedTaskIds` 只含存在任务的**刻意诚实分歧**,绝不静默吞掉悬空依赖(照搬 CC 会把悬空依赖也滤掉=丢失告警);② 纯确定性过滤/排序,无模糊启发式;③ 门控关或非数组/空数组 → 逐字节回退历史 `[blocked by: ${blockedBy.join(',')}]`(未过滤、无 `#`、逗号无空格)。门控 `KHY_TASK_BLOCKED_BY_FILTER` 默认开·关 → `buildBlockedBySuffix` 原样 `join(',')` 逐字节回退。test:taskBlockedBySuffix(**node:test**)11/11(门控梯·空/非数组·无完成全列·已完成剔除·全完成→空·数组转 Set·悬空依赖保留·数字序+字典序·数字归一·门控关字节回退·两态分歧)+ todoSnapshot(jest)13/13(新增 snapshot 完成依赖剔除 / 全完成后缀消失 / `KHY_TASK_BLOCKED_BY_FILTER=0` 字节回退原始 join 三例)无回归。三守卫过(leaf-contract taskBlockedBySuffix.js 净·agent-rules/model-hardcoding taskBlockedBySuffix.js+_taskStore.js 净)·TUI **323/323** 无回归(panel 测试 mock 掉 snapshot 不受影响)·taskDependencyGate(jest)100/100 无回归·端到端探针(四树一致:门控开 `[blocked by: #1]`(滤 #3)·门控关 `[blocked by: 3,1]`(原样)·全完成 `''`)·bundled 三副本重建(taskBlockedBySuffix.js(新)/_taskStore.js × canonical=byte-identical + 测试文件同步 + 各副本 runtime `on/off/gone` 一致)。GUARDS 红线节。
  - [x] **刀30 常驻任务清单超高截断从「盲目尾切」改「按状态生存优先级保活」(进行中/错误 > 待办 > 已完成),新纯叶子 `taskPanelLines.selectTaskLinesByPriority` 收敛 `liveRegionBudget.capTaskLines` 的截断重排,绝不再盲目 `arr.slice(cut)` 把非末尾的进行中任务挤掉(新领域:任务清单截断的「按状态优先存活」·非措辞/计数/折叠/样式)** —— 对齐 CC `src/components/TaskListV2.tsx` 的截断优先级。**背后逻辑**:CC 截断长清单前**先按状态重排** `prioritized = [...recentCompleted, ...inProgress, ...pending, ...olderCompleted]` 再 `visibleTasks = prioritized.slice(0, maxDisplay)`——保证「进行中/待办」优先于「陈旧已完成」存活,用户最该看见的活动任务绝不被长清单挤出显示。**真缺口**=Khy `liveRegionBudget.capTaskLines` 把超 `clamp(floor(rows*0.30),3,10)` 的清单**盲目尾切**(`arr.slice(cut)` 留末尾 cap 行),一个非末尾的 `→`(进行中)任务后面跟一串末尾已完成项时,进行中任务会被尾切丢掉——而它正是用户最该看见的。**可达**=默认 TUI(`KHY_FULL_TUI` 默认开)输入框上方常驻任务面板,App.js coordinated 模式经 `capTaskLines` 封顶后传 `lines`+`hidden`+`hiddenLines`。**修(SSOT 纪律)**=① `taskPanelLines.js` 新增 `selectTaskLinesByPriority(lines, cap)`——按 `_SURVIVAL_RANK {in_progress:0, error:1, pending:2, completed:3}` 升序选存活的 cap 行(**复用既有 `ICON_STATUS` 图标→状态逆映射 SSOT,不另立第二份图标表**),同档内**降序原始下标**=守 khy 既有「尾锚定」哲学(`liveRegionBudget` 注释的「尾切=实时仅显示末尾」),最后按原始顺序恢复显示;② `liveRegionBudget.capTaskLines` 门控开且全部行图标可识别 → 调它,否则回退历史 `slice` 尾切;③ `TaskListPanel.js` 尾切提示措辞经 `taskPriorityCapEnabled` 门控:开 → 去「末尾」二字(留下的是优先级最高项非末尾项),关 → 回退「仅显示末尾 N 项」。截断条数 `cut` **不变** → 面板高度恒等 → `resolveStreamReserve` 预算不变(**高度中性**,不破坏 scroll-jank reserve 不变式)。**诚实边界(刻意不纳入)**=① khy 任务行**不携带完成时间戳**,无法区分 CC 的 `recentCompleted`(≤30s)与 `olderCompleted`,故把**全部已完成**归最低生存优先级,**绝不臆造时间戳**谎称「最近完成」(照搬 CC 的 30s 分桶=伪造 khy 不持有的数据=违诚实红线);② `error(✗)` 是 CC 不具的 khy 显式状态,归入高优先级(失败需被看见);③ 任一行行首图标不可识别(非 ✓/→/✗/○)→ `selectTaskLinesByPriority` 返 `null` → `capTaskLines` 回退历史尾切(绝不在不确定状态下错排/误隐);④ 门控关 → 逐字节回退 `arr.slice(cut)` 尾切 + 措辞回退「末尾」。门控 `KHY_TASK_PRIORITY_CAP` 默认开·关 → 历史盲目尾切逐字节回退。test:taskPanelLines+liveRegionBudget(**node:test**)43/43(`selectTaskLinesByPriority`:进行中救回·同档尾锚定·跨档优先级·不可识别 null·无截断 null·trimStart;`taskPriorityCapEnabled` 门控梯;`capTaskLines`:优先级救回非末尾进行中·全同档=历史尾切·门控关回退尾切·不可识别回退尾切)+ TUI 套件(323→**324/324**,+1=`taskListPanel.test.js` 新增 `KHY_TASK_PRIORITY_CAP=0` 回退「末尾」措辞一例;另更新 3 例既有断言默认开去「末尾」)。三守卫过(leaf-contract/agent-rules/model-hardcoding taskPanelLines.js+liveRegionBudget.js+TaskListPanel.js 净)·端到端探针(四树一致:门控开救回 `→ ip`·门控关尾切 `✓ c7` 丢 `→ ip`)·bundled 三副本重建(3 源文件+3 测试文件 × canonical=byte-identical + 各副本 runtime on/off 一致)。GUARDS 红线节。
  - [x] **刀31 diff/预览「溢出折叠标记」的文本构造 + 英文复数守卫收敛到新纯叶子 `cli/previewOverflowMarker.js`,两套独立渲染引擎(Ink TUI `ToolLines.js` + 经典 REPL `toolOutputRender.js`)共用,`N=1` 时不再输出语法错的「+1 lines」「1 more row」(对齐 CC 处处守 `=== 1` 的显示约定;新领域:溢出标记文本 + 复数守卫·非措辞/计数格式/折叠/样式)** —— **背后逻辑**:CC 显示约定是所有可数名词处处守 `=== 1 ? singular : plural`。**真缺口**=这条「头部预览 + 隐藏其余 N 行,ctrl+o 展开」(展开态改说 `(capped)`)的溢出标记被**两套独立渲染引擎逐字重复**:Ink TUI `tui/ink-components/ToolLines.js::buildWriteDiffRows`(`moreText(n,sign)` 单文件 add/del 预览溢出 + 多 hunk 行数封顶 `... (diff truncated, N more rows, …)`)与经典 REPL `repl/toolOutputRender.js`(新建/删除文件预览 `... ±N lines (ctrl+o to expand)`),两处都把 `lines`/`rows` **写死复数**,`N=1`(预览恰好溢出一行/截断恰好一行)时输出语法错的「+1 lines」「1 more row」。而 khy 自己紧挨着的 gap 分隔行(`ToolLines.js:217` `line${gapBefore!==1?'s':''}`)和 `diffRenderer.js:388` 早已守复数——同一函数里**守了 gap、漏了 overflow**。**注**:刀27 扫候选时曾把此族判 NA,称「已正确守卫单复数(`diffRenderer.js:388`/`ToolLines.js:209`)」——但那次扫描只核了 **gap 分隔行**,**漏看**了同函数的 `moreText`/`rows` 溢出标记;刀31 正是补这次扫描的盲点(诚实修正,非推翻 NA)。**可达**=默认 TUI 写文件 diff 预览(`buildWriteDiffRows`)与经典 REPL(`KHY_FULL_TUI=0`)新建/删除文件预览,溢出量恰为 1 时。**修(SSOT 纪律)**=新纯叶子 `buildLinesOverflow(n, sign, expanded, env)`(`... ±N line(s) (ctrl+o to expand|capped)`)+ `buildRowsOverflow(dropped, expanded, env)`(`... (diff truncated, N more row(s), …)`),收敛标记文本 + 复数守卫,两引擎共用;各 call-site 仍自管缩进(经典 REPL `    ` 前缀)与着色(`c.dim`)。**诚实边界(刻意不纳入)**=① 门控开 `N>1` 与历史**逐字节一致**(本就复数),`N===1` 守单数(`+1 line`/`1 more row`)=唯一可见分歧;门控关 → 恒复数(legacy 字节回退);② **刻意不纳入** `toolDisplay.js:961` 的 `+N more tool uses`(另一族 marker:经典 REPL agent 工具用量计数,非 diff/预览溢出),留作后续候选,绝不顺手扩进无关 marker;③ 已正确守复数的 `ToolLines.js:217`/`diffRenderer.js:388`/`editStatLine.js`/`diffViewer.js` 各自内联 `!== 1 ? 's'` **不动**(把已正确的代码收敛进叶子=无可见输出变更的空重构,违诚实红线);④ `sign` 只认 `-` 其余归 `+`,非有限 `n` → 0。门控 `KHY_PREVIEW_OVERFLOW_PLURAL` 默认开·关 → 恒复数逐字节回退。test:previewOverflowMarker(**node:test**)14/14(门控梯·折叠/展开 N>1 字节一致·`N===1` 守单数 line·row·门控关恒复数回退·两态唯一分歧=N===1 单复数·sign 归一·非有限→0)+ TUI 套件(**324/324**,`toolLinesWriteDiff` 折叠态 `/ctrl\+o/` 断言 N=5 不受影响)+ `node --test services/backend/tests/writeDiff.test.js`(29)无回归。三守卫过(leaf-contract/agent-rules/model-hardcoding previewOverflowMarker.js 净·两 call-site leaf-contract 净)·端到端探针(四树一致:门控开 N=1 `+1 line`·门控关 `+1 lines`·rows `1 more row`)·bundled 三副本重建(previewOverflowMarker.js(新)/ToolLines.js/toolOutputRender.js + 测试文件 × canonical=byte-identical + 各副本 runtime on/off 一致)。GUARDS 红线节。
  - [x] **刀32 经典 REPL 工具追踪器「+N more tool use(s)」溢出标记的 use/uses 复数守卫,扩既有 SSOT `agentStatLine.agentMoreToolUsesLabelOr`(不新建叶子),`toolDisplay.js:961` 单 call-site 改走它,`hiddenCount===1` 时不再输出语法错的「+1 more tool uses」(对齐 CC `AgentTool/UI.tsx:639` `+{hiddenToolUseCount} more tool {hiddenToolUseCount === 1 ? 'use' : 'uses'}`;承刀31 刻意留作后续的候选)** —— **背后逻辑**:CC 处处守 `=== 1 ? singular : plural`。**真缺口**=`ToolUseTracker` 运行态(`_tools.length > _maxVisible`,默认 maxVisible=3)在末尾打印隐藏计数 `toolDisplay.js:961`,khy 历史写死复数 `+${hiddenCount} more tool uses`,`hiddenCount === 1`(恰 4 个工具)时输出「+1 more tool uses」。**SSOT 纪律(关键:不新建叶子)**=该 file 的「tool use(s)」复数早由 `cli/agentStatLine.js` SSOT(`agentToolUsesLabelOr`,门控 `KHY_CC_FORMAT`)owns(刀12 落地·已接 5 处 `Done (N tool uses…)` 统计行),本刀只**扩** SSOT 加 `agentMoreToolUsesLabelOr(count, legacy, env)`(同一 CC `=== 1` 守卫、同一 `KHY_CC_FORMAT` 门控、同一 `*Or(value, legacy, env)` 约定),`toolDisplay.js:961` 单 call-site 改走它。**诚实边界(刻意不纳入)**=① 门控开 `count>1` 与历史**逐字节一致**(本就复数),`count===1` 守单数 `+1 more tool use`=唯一可见分歧;门控关 → 恒 call-site legacy `+${count} more tool uses`(字节回退);② **复用 `KHY_CC_FORMAT`**(不新设门控)=与 agentStatLine 全族一致的刻意选择(该 marker 单复数本就是同一 CC tool-use 显示约定);③ **不**把 `agentToolUsesLabelOr`(返回整句 `1 tool use`/legacy)硬塞进带 `+…more` 前缀的标记(语义不同),也**不**另起全局 pluralize 工具(收敛别处已正确的内联守卫=空重构,违诚实红线)。门控 `KHY_CC_FORMAT` 默认开·关 → 恒复数逐字节回退。test:agentStatLine(**node:test**)16/16(新增 `agentMoreToolUsesLabelOr` 门控开 count===1 守单数 / count>1 legacy / 门控关恒 legacy / 默认门控)+ 消费者回归 `agentTreeView`·`agentTreeController`·`agentStatsService`(jest **48/48**)+ 端到端 ToolUseTracker 探针(门控开 4 工具 → `+1 more tool use`·门控关 → `+1 more tool uses`)。三守卫过(leaf-contract/agent-rules/model-hardcoding × agentStatLine.js+toolDisplay.js 净)·bundled 三副本重建(agentStatLine.js/toolDisplay.js/agentStatLine.test.js × canonical=byte-identical + 各副本 runtime on/off/N>1 一致)。GUARDS 红线节。
  - [x] **刀33 上下文窗口**占用率**喂入值漏算缓存段,新纯叶子 `cli/contextResidentTokens.js::contextResidentTokensOr(tokenUsage, legacyBase, env)` 把 `cacheReadInputTokens`+`cacheWriteInputTokens` 加回未缓存 `inputTokens` 基数,两处 `setContextUsage`(`ai.js:6286`、`repl.js:6784`)改走它(对齐 CC `src/utils/context.ts::calculateContextPercentages` 的 `totalInputTokens = input_tokens + cache_creation_input_tokens + cache_read_input_tokens`)** —— **背后逻辑**:占用率分子必须**三段都算**,命中的缓存 token 仍**占据**上下文窗口、只是计费便宜,没从窗口消失。**真缺口**=两处 `setContextUsage` 只喂未缓存 `tokenUsage.inputTokens` 一段,把同一对象上早已携带的 `cacheReadInputTokens`/`cacheWriteInputTokens`(经 `services/gateway/adapters/_cacheUsage.js` 自 `cache_read_input_tokens`/`cache_creation_input_tokens` 规范而来)整段丢弃(repl.js 注释还写死错误心智模型「actual tokens sent as context」)→ 开 prompt cache 的 Claude-Direct 链路上,一轮缓存命中 turn `inputTokens:1.2k`+`cacheRead:78k` 占用率被**低估一个数量级**(实测 1% 应为 41%):HUD 上下文条整段会话停近空绿、`/context` 面板显 1%、且喂同一 `state.contextWindow.used` 的自动压缩告警(`contextWarning`,repl.js:1831/1895)几乎永不触发。**全 5 处消费者共源**=HUD 条(`hudRenderer.renderContextBar`)/两处自动压缩(`buildContextWarning`)/`/context` 面板(`repl.js:3837` + `router.js:1327` 的 `computeContextStats`)都读 `hud.getState().contextWindow.used`,故单点修喂入值即修全部。**SSOT 纪律**=无既有叶子求这三段和(别处 `totalInputTokens` 指会话累计非窗口占用),新建纯叶子(零 IO 零业务 require、确定性、`*Or(value,legacy,env)` 镜像 agentStatLine/ccFormat 约定):门控开→`legacyBase + 读缓存 + 写缓存`,门控关→原样 `legacyBase`;三段在 Anthropic usage 互不相交(khy `state.totalInputTokens += input_tokens` 只累未缓存段佐证)相加不重复计数。**诚实边界**=① 真缺口(数据在手只漏算)非臆造不持有数据;② 只动喂入值不动 call-site `if(inputTokens>0)` 触发守卫(真实 turn `inputTokens` 几无 0·改守卫=扩面);③ 门控关→各 call-site 逐字节回退原 `inputTokens`(ai.js 传 `tokenUsage.inputTokens`、repl.js 传 `inputTokens||promptTokens||0`,故由 call-site 传 base 而非叶子写死,字节回退绝不串味);④ `computeContextStats` 的 `round(used/limit*100)` 公式本就与 CC 一致不动,本刀只修被喂进的 `used`。门控 `KHY_CONTEXT_CACHE_TOKENS` 默认开·关→恒 legacyBase 逐字节回退。test:contextResidentTokens(**node:test**)12/12(门控梯 / 三段相加 81.2k / 无缓存=legacyBase / 仅读缓存 / 门控关恒 legacyBase / 唯一分歧=缓存段 / 防呆 base 非数·缓存段负·null usage / 默认门控)+ 端到端 `setContextUsage→computeContextStats` 探针(门控开 81200→41% healthy·门控关 1200→1%)+ `hudTokensSsot`(node:test)3/3 无回归(直喂字面值不经 call-site)。三守卫过(leaf-contract/agent-rules/model-hardcoding × contextResidentTokens.js+ai.js+repl.js 净·repl.js agent-rules 1warn 预存)·bundled 三副本重建(contextResidentTokens.js/ai.js/repl.js/测试 × canonical=byte-identical + 4 树 require 一致)。GUARDS 红线节。下一候选(part b/c 续):上下文占用域已收口(刀33);单/复数/计数标记族已穷尽(刀31/32);回 part b 前端逐项对齐或新探更高价值显示逻辑发散。
  - [x] **刀34 权限对话「始终允许(allow-always)」选项标签的动词对所有工具硬编码成 "reading",新纯叶子 `cli/ui/alwaysAllowLabel.js::buildAlwaysAllowLabelOr(toolName, legacyLabel, env)` 按工具族选动词,单工具对话 `cli/ui/permissionDialog.js:612` 改走它(对齐 CC 按工具族分流权限对话:`FilePermissionDialog/permissionOptions.tsx` 写/编辑→"all edits"、`BashPermissionRequest/bashToolUseOptions.tsx` bash→命令族)** —— **背后逻辑**:`allow-always` 实际授予 `permissionStore.approve(permissionKey,'forever')`(永久放行**该工具**),option-2 标签必须忠实描述该工具的真实操作。**真缺口**=`permissionDialog.js` 单工具对话把 option-2 标签硬编码成 `Yes, allow reading from <project> from this project`,对**所有**工具(含 Write/Edit 改文件、Bash 任意命令如 `rm -rf`)都说 "reading",把改写/危险操作描述成只读授权(用户可见的危险误导)。**SSOT 纪律**=新纯叶子(零 IO、零业务 require、零 chalk、确定性、`*Or(value,legacy,env)` 镜像 agentStatLine/ccFormat 约定),按 `formatToolContent` 同口径归一(`toLowerCase().replace(/[\s_-]/g,'')`)选动词:写/编辑族(write/edit/multiedit/notebookedit/scaffold…)→"all edits"、bash 族(bash/shellcommand/command)→"running commands"、只读类(read/grep/glob/ls/webfetch)→保留 legacy "reading";叶子返回含 `{project}` 占位符的纯字符串,call-site `.replace('{project}', chalk.bold(projectName))`(chalk 仍在 call-site,叶子纯净)。**诚实边界**=① 真缺口(同一标签对改写/危险操作谎称只读)非 no-op;② khy **无法复刻** CC 的 Haiku 实时生成命令前缀建议(模型调用·伪造前缀=臆造)→ bash 只用真实通用措辞 "running commands"(忠实描述「永久放行 bash 工具」),**绝不臆造 per-command-prefix 作用域**;③ **只覆盖明确错且危险的两族**(写/编辑、bash),与 CC 两个专用对话同构·read/search/fetch 等只读类 "reading" 本就成立门控开也不动;④ `formatBatchPermissionDialog` 的 "always trust from <project>" 本就动词中性不纳入。门控 `KHY_ALWAYS_ALLOW_LABEL` 默认开·关→原样返回 call-site 传入 legacyLabel(逐字节回退)。test:alwaysAllowLabel(**node:test**)10/10(门控梯 / 写编辑族→all edits 含归一变体 / bash 族→running commands / 只读类保留 reading / 门控关恒 legacy / legacy 原样透传不串味 / 防呆 null·undefined·数字 toolName / 默认门控)+ 端到端 call-site `.replace` 探针(门控关 write/bash/read 三族 byte-identical 现状·门控开 write→"all edits"·bash→"running commands"·read→"reading")。回归:`permissionDialogResolve`+`permissionsPromptL2Options`+`permissionReply`(node:test)20/20 无破坏(全树无任何测试断言 option-2 标签文本)。三守卫过(leaf-contract/agent-rules/model-hardcoding × alwaysAllowLabel.js+permissionDialog.js 净)·bundled 三副本重建(alwaysAllowLabel.js/permissionDialog.js/测试 × canonical=byte-identical sha256 + 4 树 require 一致)。GUARDS 红线节。下一候选(part b/c 续):权限对话动词域已收口(刀34);回 part b 前端逐项对齐或新探更高价值显示逻辑发散。
  - [x] **刀35 spinner token byline 数字格式 `cli/spinner.js:167` 内联 `/1000.toFixed(1)` 是 token 格式器同族迁移漏掉的最后一支孤儿,直接 route 进既有 `ccFormatTokens` SSOT(对齐 CC `SpinnerAnimationRow.tsx` 的 `formatNumber`)** —— **背后逻辑**:token 数字应紧凑记数(`>10k` 不钉 ".0"、百万级进 "m")。**真缺口**=spinner `fmtTokens` 闭包旧规则 `n>=1000 → (n/1000).toFixed(1)+'k'` else `String(n)` 会显 `45.0k`(应 `45k`)、`128.0k`(应 `128k`)、且无 mega 单位停在 `1500.0k`(应 `1.5m`);而同族其余 call-site(`hudRenderer.fmtTokens`/`turnStats.fmtTokens`/`agentTokensLabelOr`)早已迁 `ccFormatTokens`——`hudRenderer.fmtTokens` 注释甚至自证「旧本地格式器 dropped the decimal above 10k 且 no mega unit」=本刀正是补该注释点名的孤儿。**SSOT 纪律(关键)**=**不新建叶子**(`ccFormatTokens` 已是 SSOT,新建=重复违红线),直接把 spinner 内联闭包 route 进 `ccFormatTokens`,镜像 `hudRenderer.fmtTokens` 的 wrapper(`ccFormatEnabled(process.env)` 判→`ccFormatTokens(n)`,非有限 `''` 回退 legacy)。**诚实边界**=① 真缺口(孤儿,非 no-op);② 收敛到既有 SSOT 非新建重复;③ `spinnerMeta.buildStatusParts` 收的是**已格式化**字符串(`'↑ 1.2k tokens'`),本刀只改喂入前的数字格式器,assembly 层不动→`spinnerMeta.test.js` 传预格式化串故无回归;④ 门控关 / 非有限 → 逐字节回退 spinner **自己的**历史规则(`n>=1000 → toFixed(1)k` else `String(n)`,**不**串入 `hudRenderer` 略不同的 `n>=10000 → toFixed(0)` legacy,避免跨 call-site 串味)。门控 `KHY_CC_FORMAT`(同族门控)默认开·关→逐字节回退。验证:`spinnerMeta.test.js`(node:test)**9/9** assembly 无回归 + 门控关 byte-identity 探针(`KHY_CC_FORMAT=off` 时 wrapped(n) === spinner legacy(n) 对 5/500/1000/45000/128000/1500000 全等)+ 门控开发散探针(45000→"45k"·128000→"128k"·1500000→"1.5m")+ spinner.js require/parse OK。守卫过(agent-rules/model-hardcoding × spinner.js 净;spinner.js 非纯叶子不跑 leaf-contract)·bundled 三副本重建(spinner.js × canonical=byte-identical sha256 + 4 树 require 一致)。**无新叶子无新测试**(SSOT `ccFormatTokens` 已测、assembly `spinnerMeta` 已测)。GUARDS 红线节。下一候选(part b/c 续):token 数字格式器家族已彻底收口(刀35 补完最后孤儿);回 part b 前端逐项对齐或新探更高价值显示逻辑发散。
  - [x] **刀36 `/context`/`/status` 面板的裸 token 数字散落 `router.js:1338-1340`(英文三行)+ `repl.js` 5 处中文面板内联 `(x/1000).toFixed(1)k`(limit `toFixed(0)k`),收敛进既有 SSOT 模块 `ccFormat` 新增的标准 `*Or` 兄弟 `ccFormatTokensOr(count, legacy, env)`(对齐 CC 上下文面板的 `formatTokens`)** —— **背后逻辑**:面板 token 数字应紧凑记数(`>10k` 不钉 ".0"、百万级进 "m"),与刀35 spinner 同口径同门控。**真缺口**=`router.js` 的 `Used/Remaining/Session` 三行 + `repl.js` 的 `已使用/剩余/会话令牌/令牌用量` 多处显 `Used: 128.0k`(应 `128k`)、1M 上下文 limit 显 `1000k`(应 `1m`),与早已迁 `ccFormatTokens` 的 spinner/hud/turnStats 族不一致。**SSOT 纪律(关键)**=混合 legacy(`.toFixed(1)k` 数据图 / `.toFixed(0)k` limit 图)需 per-site legacy,故走 `*Or(value, legacy, env)` 约定(镜像 `ccFormatCostOr`/`ccBriefTimestampOr`);`ccFormatTokens` 此前**无** Or 变体、`agentTokensLabelOr` 烤死 " tokens" 后缀不可裸嵌→给 `ccFormat` 加标准 `ccFormatTokensOr`(门控开→`ccFormatTokens(count)`,非有限 `''` 回退 legacy;门控关→原样返 legacy)=**收敛到既有 SSOT 模块非新建重复叶子**。`router.js` 用两个局部 `tk1`/`tk0`、`repl.js` 用模块级 `_tk1`/`_tk0`/`_tkSpin`(避免 5 处重复定义)把全部 token 图 route 进它。**诚实边界**=① 真缺口(与刀35 同类·token 数字未走 SSOT)非 no-op;② 收敛到既有 SSOT 模块非新建重复;③ `/context` 面板**布局**(中文标签、无 CC 的 `⛁/⛀/⛶` 方格网格)是 khy 刻意不同设计·本刀**只动数字 humanize 口径不碰布局**(照搬网格需 category 分解数据 khy 不携带=臆造·刻意不做);④ **仅 token 图(k 后缀);时长(`s` 后缀,如 `elapsed/1000).toFixed(1)s`)绝不纳入**(那是 `ccFormatDuration` 族另一刀);⑤ 门控关→各 call-site 逐字节回退**自己的** legacy(`tk1`=`.toFixed(1)k`、`tk0`=`.toFixed(0)k`、`_tkSpin`=`n>=1000?toFixed(1)k:String(n)`·绝不串味)。门控 `KHY_CC_FORMAT`(同族门控)默认开·关→逐字节回退。验证:`ccFormat.test.js`(node:test)**14/14**(含 `ccFormatTokensOr` 门控开裸数字 45000→"45k"/128000→"128k"/1500000→"1.5m"/200000→"200k" + 门控关 call-site legacy 逐字节 + 非有限回退 + 默认门控)+ 三 helper 门控关 byte-identity 探针(`tk1/tk0/_tkSpin` 对 0/500/1000/12000/45000/200000/1000000/1500000 全等原 `.toFixed`)+ token 消费者回归 **19/19**(hudTokensSsot/tokenCountReportSsot/ccFormatCost)。守卫过(leaf-contract × ccFormat.js 净·agent-rules/model-hardcoding × router.js/repl.js 净;repl.js agent-rules 1warn=`no-opaque-status`@3191 预存非本刀引入)·bundled 三副本重建(ccFormat.js/router.js/repl.js/ccFormat.test.js × canonical=byte-identical sha256 unique=1·4 树 `ccFormatTokensOr=function` 一致)。GUARDS 红线节。下一候选(part b/c 续):token 数字格式器家族(spinner 刀35 + 面板刀36)已彻底收口;回 part b 前端逐项对齐或新探更高价值显示逻辑发散。
  - [x] **刀37 粘贴胶囊 `[Pasted text #N +M lines]` 的 M 三处各写 `text.split('\n').length`(= 行数 = CC 值 +1、CRLF 多计残留 \r、裸 CR 当 1 行),新纯叶子 `cli/pastedRefLines.js::pastedRefLineCountOr(text, legacy, env)` 移植 CC `getPastedTextRefNumLines` 为单一真源(对齐 CC `src/history.ts`)** —— **背后逻辑**:CC 那个 **M** 不是「行数」而是**换行符个数** `(text.match(/\r\n|\r|\n/g)||[]).length`,CC 源注释逐字「`line1\nline2\nline3` → **+2 lines(不是 3)**,We preserve that behavior」——胶囊里的 "+M" 是「首行**之外**额外多少行」的增量记号(无尾随换行 M=行数−1),且 `\r\n`/裸 `\r`/`\n` 各归一计一次。**真缺口**=三处 `repl.js:2851`(`_injectPasteIntoLine`)/`repl.js:2875`(`_storePendingPaste`)/`repl/busyInputClassifiers.js:24`(忙队列预览)各 `split('\n').length` → 每次多行粘贴胶囊**多报一行**(3 行粘贴显 `+3` 应 `+2`),CRLF 粘贴 `split('\n')` 既多计又残留 `\r`、老式 Mac 裸 `\r` 整段当 1 行。**SSOT 纪律**=CC 有专用 SSOT `getPastedTextRefNumLines`·新纯叶子(零 IO、确定性、`*Or(value,legacy,env)` 镜像 ccFormatTokensOr/ccFormatCostOr 约定)移植之·三处共源。**诚实边界**=① 真缺口(CC 专用 SSOT·khy 三处各多报一行+CRLF 误计)非 no-op;② **`repl.js:2875` 的 `lineCount`(split 行数)刻意保留**驱动两处**非显示**判定:`isShort`(≤5 行内联 vs 折叠的**产品决策**)+ 中文提示 `已折叠粘贴 N 行`(语义=**总行数**非 "+N delta"·改成换行数会把 3 行说成「2 行」=错)——只为标签新增 `_displayLines` 走 CC 口径·刻意**不**收敛 isShort/中文提示(度量的是别的东西·tag 与 hint 数字刻意各异:tag CC delta、hint 总行数);③ `busyInputClassifiers` 无 bare 分支·门控开且换行数 0(单行巨贴)→ 照 CC `formatPastedTextRef` 走裸 `[Pasted text]`(不显 "+0 lines")·门控关逐字节回退历史无条件 `+N lines`;④ **分支判定保持各 call-site 原条件**(repl.js `lineCount>1` ⟺ CC `numLines>0`·bare/非-bare 边界恒等→只换显示数字不动分支);⑤ tag/expand 正则匹配 `\+\d+ lines` 与数值无关、展开按 `#id` 查 `_pasteArchive`·改显示数字不破坏标签剥离/展开;⑥ 门控关→各 call-site 逐字节回退原 `split('\n').length`。门控 `KHY_PASTED_REF_LINES` 默认开·关→逐字节回退。验证:`pastedRefLines.test.js`(node:test)**9/9**(换行数 "+2 not 3" / CRLF·裸 CR 归一 / 尾随换行计入 / 防呆 null·非串·空 / `*Or` 门控梯 / 门控开关唯一分歧=计数口径 2vs3 / 默认门控)+ `busyInputClassifiers.test.js`(**jest 27/27**:改默认开断言 CC tally `+2`/`+1`·新增门控关 byte-identity 回退 `+3`·全树仅此文件断言粘贴标签)+ 端到端门控开/关探针(5 行体 OFF`+5`/ON`+4`·CRLF 3 行体 OFF`+3`/ON`+2`)。三守卫过(leaf-contract × pastedRefLines.js 净·agent-rules/model-hardcoding × pastedRefLines.js+busyInputClassifiers.js 净)·bundled 三副本重建(pastedRefLines.js/repl.js/busyInputClassifiers.js/两测试 × canonical=byte-identical sha256 unique=1·4 树 `pastedRefLineCountOr=function` 一致)。GUARDS 红线节。下一候选(part b/c 续):粘贴胶囊行数口径已收口;回 part b 前端逐项对齐或新探更高价值显示逻辑发散。
  - [x] **刀38 写文件结果摘要 `已写入 <file>（… 行，<size>）` 里的 size 仍直接内联裸 `${result.bytes}B`,是 `ccFormatFileSize` 同族漏掉的最后一支孤儿,收敛进既有 SSOT(对齐 CC `formatFileSize`)** —— **背后逻辑**:字节数 → 人类可读应走 `ccFormatFileSize`(`30 bytes`/`5KB`/`1.5MB`),与「图片大小展示一律走 ccFormatFileSize」同口径。**真缺口**=同模块 `toolResultSummary.js` 的 image-read 摘要(:206)、webfetch 摘要(:267)都已 route 进 `ccFormatFileSize` SSOT,**唯独写摘要(:360-361)仍打印裸 `${result.bytes}B`**——大文件 `51200B` 完全不可读、且与同模块另两处不一致(平行于刀35 spinner token 孤儿)。**SSOT 纪律**=**不新建叶子**(`ccFormatFileSize` 已是 SSOT,新建=重复违红线),新增模块级 `_writeSizeDisplay(bytes, env)`(门控开→`ccFormatFileSize(bytes) || ${bytes}B`;门控关→`${bytes}B`),镜像同文件 image/webfetch 的 try/catch require 模式,把写分支两处 `${result.bytes}B` route 进它。**诚实边界**=① 真缺口(同模块另两处早走 SSOT·写摘要是孤儿)非 no-op;② 收敛到既有 SSOT 非新建重复;③ **保留「显示大小」既有行为**(只把裸 `NB` humanize·绝不删 size 段);④ 小文件 `30B → 30 bytes` 是 CC 同族口径(与 image/webfetch 一致)非退化·真正收益在大文件(`51200B → 50KB`);⑤ 门控关 / require 失败 / 空返回 → 逐字节回退 `${bytes}B`。门控 `KHY_WRITE_SIZE_CCFORMAT` 默认开·关→逐字节回退。验证:`toolResultSummary.test.js`(**jest 65/65**:含 sub-1KB→`N bytes` / 大文件 51200→`50KB` / 小数 1536→`1.5KB` / 门控关 byte-identity `NB` 回退 + 既有 8 处写摘要断言同步更新为 `N bytes`)+ 端到端门控开/关探针(51200 ON`50KB`/OFF`51200B`)。守卫过(agent-rules/model-hardcoding × toolResultSummary.js 净;非纯叶子不跑 leaf-contract)·bundled 三副本重建(toolResultSummary.js/测试 × canonical=byte-identical sha256 unique=1·4 树 `summarizeToolResult=function` 一致)。**无新叶子无新测试模块**(SSOT `ccFormatFileSize` 已测)。GUARDS 红线节。下一候选(part b/c 续):`ccFormatFileSize` 字节 humanize 家族(image/webfetch/write 三处)已彻底收口;回 part b 前端逐项对齐或新探更高价值显示逻辑发散。
  - [x] **刀39 用户可见英文计数串硬编码复数形,`n === 1` 显「1 matches」/「1 lines」/「1 files」=英文语法错;移植 CC `stringUtils.ts::plural` 为纯叶子 `ccPlural`,三处用户可见 call-site 收敛(对齐 CC `plural`)** —— **背后逻辑**:CC 用 `plural(n, word, pluralWord = word + 's') => n === 1 ? word : pluralWord` 取代散落的内联 `word${n===1?'':'s'}` 习语,统一英文计数串单复数,**严格 `=== 1`**(string `'1'` 取复数,不强转)。**真缺口**=Khy 多处用户可见计数串硬编码复数形,`n === 1` 时显「1 matches」「1 lines」「1 files」——`handlers/session.js:596`(搜索结果头 `Search Results (N matches)`)、`atMentionInject.js:125`(`@dir` 注入 Read 行 sizeInfo `N files`)、`toolDisplay.js:392`(经典 REPL `✏️  Writing` 行 `(N lines)`),三处均 reachable(search 仅上文 `results.length === 0` 守卫)。**SSOT 纪律**=全树确认**无既有 plural SSOT**(非重复),新建纯叶子 `cli/ccPlural.js`(零 IO·零业务 require·`plural` 逐字节移植 + `pluralOr(n,word,pluralWord?,env)` 门控包装:门控开→CC plural;门控关→**复数形**=各 call-site 历史硬编码形逐字节回退,因本族 legacy 一律复数)。**诚实边界**=① 真缺口(`n===1` 显「1 matches」是确凿英文语法错)非 no-op;② 移植 CC 真实 SSOT 非编造;③ 无既有 plural SSOT 非重复叶子;④ **刻意排除**动词一致串(`permissionDialog.js:650`「N tools require approval」,`n===1` 应「1 tool requires approval」,名词-only 叶子产「1 tool require approval」仍错·且非 CC 组件移植)+ 诊断命令(verify.js errors/warnings·窄范围)+ 混合类型串(ai.js:4845 `fp.lines` 可能为 `'?'`);⑤ 门控关 → 复数形逐字节回退。门控 `KHY_CC_PLURAL` 默认开。验证:`ccPlural.test.js`(**node:test 8/8**:门控梯 / `plural` n===1 单数·0,2 复数 / 自定义 pluralWord match→matches / 严格 ===1 字符串取复数 / `pluralOr` 门控开分歧·门控关恒复数 byte-identity / 唯一分歧在 n===1 / 默认门控开)+ 端到端门控开/关探针(ON「1 match」/「1 line」/「1 file」·OFF「1 matches」/「1 lines」/「1 files」)。守卫过(leaf-contract × ccPlural.js·agent-rules/model-hardcoding × ccPlural.js+session.js+atMentionInject.js+toolDisplay.js 全净)·bundled 三副本重建(ccPlural.js/测试 + 三 call-site × canonical=byte-identical sha256 unique=1·4 树 `pluralOr=function` 一致)。GUARDS 红线节。下一候选(part b/c 续):英文计数串单复数族(session/写显示/`@dir` 三处名词-only)已收口;permissionDialog 动词一致串诚实 NA;回 part b 前端逐项对齐或新探更高价值显示逻辑发散。
  - [x] **刀40 TUI 工具失败详情每行固定 `truncate(ln,120)`,展开(Ctrl+O)态仍裁 120 → 超长错误行尾部永看不到、且无视终端宽;改走既有 `diffClipWidth` SSOT,与姊妹 literal stdout 分支(刀17)同口径(对齐 CC verbose 全展)** —— **背后逻辑**:工具失败详情行与 diff/literal 行同属「随终端列宽 + Ctrl+O 展开态自适应裁切」家族——展开应 `diffClipWidth→Infinity`(整行交 ink 自然换行、Ctrl+O 真全貌),折叠应裁到终端感知宽(避免裁出宽于 TTY 的行被 ink 二次换行撑破折叠行预算)。**真缺口**=`ToolLines.js` 错误块 `detailLines.forEach(... truncate(ln,120))` 固定 120,与同文件 literal stdout 分支(刀17 已修成 `diffClipWidth`,gutterWidth=4)不一致:① 80 列 TTY 上裁 120 仍宽于终端;② **展开后仍裁 120** → 超长错误行(长路径 ENOENT/长 JSON 校验行/长编译诊断)尾部 Ctrl+O 永看不到,是 `toolErrorFold`「诚实展开」自述原则漏掉的最后一支(literal 姊妹刀17 已修、error 这支当时漏过)。**SSOT 纪律**=route 进**既有** `diffClipWidth` SSOT,**不新建叶子**(新叶子=重复违红线);复用既有门控 `KHY_TOOL_ERROR_FOLD`(整个多行错误忠实渲染特性同一开关)不新增开关;gutterWidth=3(外层工具块 marginLeft 1 + err 块 marginLeft 2、错误行无 `⎿ ` 前缀);沿用 `truncate(ln, _errClipW)` 与 literal 姊妹完全一致。**诚实边界**=① 真缺口(展开态仍裁 120 → 超长错误行尾部 Ctrl+O 看不到)非 no-op(回归实证:gate ON+expanded 整行可见无 `…`、gate ON+collapsed 终端感知截尾带 `…`,两态可见差异);② route 既有 SSOT 非新建重复;③ 复用既有门控不新增开关;④ **刻意排除行内空白保留**——错误行行内空白早由 `stripInternalControlText`(preserveNewlines 分支 `.map(ln=>ln.replace(/[ \t]+/g,' ').trim())`)逐行折叠 + trim(刀18 刻意:保留行间结构、归一行内空白),属**上游既定设计**,绝不在渲染层「恢复」缩进(那会改刀18 既定形态、违「绝不改 khy 刻意不同代码」红线)=**honest-NA**,本刀只治宽度/展开一面;⑤ 门控关 → 固定 `truncate(ln,120)` 逐字节回退。门控 `KHY_TOOL_ERROR_FOLD` 默认开。验证:`node scripts/run-ink-tui-tests.js`(**327/327**,含 `tests/tui/toolLinesErrorFaithful.test.js` 3/3:gate ON+expanded 整行无 `…`、gate ON+collapsed 终端感知截尾带 `…`、gate OFF+expanded 固定 120 截尾带 `…`)。守卫过(agent-rules/model-hardcoding × ToolLines.js 全净;TUI 组件**非纯叶子不跑 leaf-contract**)·bundled 三副本重建(ToolLines.js/测试 × canonical=byte-identical sha256 unique=1·三树 require OK)。**无新叶子**(收敛到既有 `diffClipWidth`)。GUARDS 红线节。下一候选(part b/c 续):工具失败详情行已与 diff/literal 收口到 `diffClipWidth` 同 SSOT;回 part b 前端逐项对齐或新探更高价值显示逻辑发散。
  - [x] **刀41 spinner token byline 误用 `ccFormatTokens`(去 `.0`,显 `8k`/`45k`/`1m`),与 CC `formatNumber`(保 `.0` → `8.0k`/`45.0k`/`1.0m`)+ 同文件 legacy 回退 + agentStatLine 自述同族铁律三方背离;换 `ccFormatNumber`(对齐 CC `SpinnerAnimationRow.tsx:178`)** —— **背后逻辑**:CC spinner byline `const tokenCount = formatNumber(totalTokens)`——`formatNumber` 对 n≥1000 钉 `minimumFractionDigits:1` **保留尾随 `.0`**(8000→`8.0k`、45000→`45.0k`、1000000→`1.0m`);`formatTokens`=`formatNumber(n).replace('.0','')` 去 `.0`,**二者非同一 helper**。**真缺口**=`cli/spinner.js` `fmtTokens` 闭包误接 `ccFormatTokens`,与三方背离:① CC `formatNumber`;② 同文件 legacy 回退 `(n/1000).toFixed(1)k`(本就保 `.0`,即迁移到 `ccFormatTokens` 才引入背离);③ `agentStatLine.js:8-15` 自述同族铁律「`AgentProgressLine`/`TeammateSpinnerLine`/`CoordinatorAgentStatus`/**`Spinner`** 全走 `formatNumber`(注意是 formatNumber 而非 formatTokens,保留尾随 `.0`)」——spinner 是该族唯一被接错的一支(且 spinner 自身注释声称对齐 `formatNumber` 却给 `formatTokens` 示例 `45000→45k`,注释意图与代码/示例自相矛盾)。**SSOT 纪律**=route 进**既有** `ccFormatNumber` SSOT,**不新建叶子**;复用既有门控 `KHY_CC_FORMAT`(同族门控)不新增开关。**诚实边界**=① 真缺口(整千/整百万值丢 `.0`,三方背离)非 no-op(回归实证:gate ON 8000→`8.0k tokens` 不再 `8k tokens`、1000000→`1.0m`);② route 既有 SSOT 非新建重复;③ 复用既有门控不新增开关;④ **刻意排除** `hudRenderer`/`turnStats` 的 `fmtTokens`(走 `ccFormatTokens`=khy 自有面、与 CC `StatusLine`/`GoalPill` 用 `formatTokens` 同口径,各自正确)+ `↑ input / ↓ output` 双箭头(CC 仅 `↓ total`,khy 刻意多给信息非缺陷)+ effort 低/中档裸词 `· medium`(khy 自有中文动词轮换体系,无 CC 可复制确定性结构,不强塞);⑤ 门控关 / 非有限 → legacy `(n/1000).toFixed(1)k` 逐字节回退(亦保 `.0`)。门控 `KHY_CC_FORMAT` 默认开。验证:`node --test tests/cli/spinnerTokenByline.test.js`(**4/4**:gate ON 8000→`8.0k`·1000000→`1.0m`·12345→`12.3k`(非整千值与 formatTokens 同·无回归)·gate OFF legacy `8.0k`;探针拨 `_startTime` 至 31s 前越过 30s reveal-gate、stub `process.stdout.write` 捕获帧)+ 相关回归(spinnerMeta/turnStats/hudTokensSsot node:test **26/26**·ab_test_interactivity+ab_interactivity_v2 **110/111**,唯一 fail=GAP-2 `_formatToolResult` 截断警告=**预存**与本刀无关)。守卫过(agent-rules/model-hardcoding × spinner.js 全净;**非纯叶子不跑 leaf-contract**)·bundled 三副本重建(spinner.js/测试 × canonical=byte-identical sha256 unique=1·三树 `DynamicSpinner=function`)。**无新叶子**(收敛到既有 `ccFormatNumber`)。GUARDS 红线节。下一候选(part b/c 续):spinner/agent token-byline 家族已彻底收口到 `formatNumber`/`formatTokens` 正确分工;回 part b 前端逐项对齐或新探更高价值显示逻辑发散。
  - **下一候选**(part b/c 续):mailto + URL 自我重复刀11 已收口。markdown 渲染域既有缺口已穷尽核实(blockquote/下划线/表格换行/有序列表/紧致/链接展示);agent 树域续了统计行(刀12)+ liveness 占位(刀13)+ **时长 elapsed 控制器路径走时长 SSOT(刀20)**。spinner reveal-gate 已两前端收口(刀14)。**时长格式器家族已收口最后一支孤儿:spinner/step/task 面板 `_formatElapsed` 走 ccFormatDuration(刀21)**。diff/输出域已收口六刀:diff 内容列裁切宽度(刀15)+ 词级 changeRatio 度量口径(刀16)+ literal stdout 裁切宽度复用同 SSOT(刀17)+ **工具错误详情多行折叠(刀18·补刀17 漏过的错误行)**+ **diff/预览溢出标记文本+复数守卫收敛 `previewOverflowMarker`,N=1 不再「+1 lines」(刀31·两引擎共用·补刀27 扫描漏看的 overflow marker)**。task-panel 域已续隐藏项摘要按状态分解(刀19)+ **已完成行 strikethrough 对齐 CC(刀23·整行单 Text 划线绕开拆节点风险,解了刀19 的暂缓)**+ **头行按状态计数对齐 CC `TaskListV2` isStandalone 头行(刀28·新叶子 `taskPanelHeader`·消费 `countTaskLinesByStatus` 图标逆映射 SSOT·计数覆盖全量·error 诚实单列)**+ **快照行 blocked-by 后缀只列仍未完成依赖、已完成剔除全完成则消失(刀29·新叶子 `taskBlockedBySuffix`·对齐 CC `openBlockers`·保留 khy 悬空依赖告警的刻意分歧·快照层显示对齐 `canStart` 判定不再自相矛盾)**+ **超高截断从盲目尾切改按状态生存优先级保活(刀30·新叶子 `taskPanelLines.selectTaskLinesByPriority`·对齐 CC 截断优先级 `prioritized.slice`·进行中/错误 > 待办 > 已完成·同档守 khy 尾锚定·高度中性·绝不臆造完成时间戳照搬 CC 的 recentCompleted 30s 分桶)**。**工具头 arg-summary 长度上限域:Bash `command` 头对齐 CC `MAX_COMMAND_DISPLAY_CHARS=160`(刀22·新叶子 `toolHeaderCap`)**。**TUI 补全菜单过滤域:斜杠菜单走 `rankSlashCommands` 子串/分段/描述匹配(刀24·新叶子 `slashMenuFilter`·消费既有 SSOT 与经典 REPL 收敛)**。**经典 ANSI 编辑 diff 域:`renderStructuredDiff` 改走真分 hunk 消费既有 `computeStructuredDiffHunks` SSOT、与默认 TUI 收敛(刀25·绝不再 churn 重绘未改行/虚报 ±计数)**。**工具失败串受众拆分域:给「人」的入参校验失败串折成单行 `Invalid tool parameters`、完整分组只给「模型」+Ctrl+O(刀26·新叶子 `ccUserFacingToolError`·消费产串方自识别签名 `ccValidationError.isValidationErrorMessage`)**。**工具头「读取范围」算术域:Read 头行路径后追加 `offset`/`limit`→`第 X-Y 行` / `从第 X 行起`(刀27·新叶子 `readRangeSuffix`·两处头渲染收敛·绝不臆造 CC 的 `pages` 分支因 Khy read 无 pages 参数)**。下一候选(part b/c 续):`toolDisplay.js:961` 的 `+N more tool uses`(经典 REPL agent 工具用量计数 N=1 复数·刀31 刻意留作后续)·回到 part b 前端逐项对齐或新探一处更高价值的显示逻辑发散。**已穷尽核实归档**:ccFormat/format.ts 族(刀3-7)·blockquote 正文(刀8)·下划线强调(刀9)·diff 整宽色条经典路径(刀10)·markdown 链接展示 mailto/去重(刀11)·agent 统计行三分段单复数/紧凑记数/h 进位(刀12)·agent liveness 活动占位(刀13)·spinner 计时+token 30s reveal-gate ink TUI 收口(刀14)·diff 内容列裁切宽度终端感知+展开换行(刀15)·diff 词级 changeRatio 字符长度口径(刀16)·literal stdout 裁切宽度复用 diffClipWidth(刀17)·**工具错误详情多行折叠 10+页脚+Ctrl+O 显全(刀18)**·**任务清单隐藏项摘要按状态分解(刀19)**·**agent 树时长 elapsed 控制器路径走 `agentDurationLabelOr` SSOT(刀20)**·**spinner/step/task 面板秒级时长 `_formatElapsed` 走 `ccFormatDuration` SSOT(刀21·时长家族最后一支孤儿)**·**工具头 Bash `command` 长度上限对齐 CC `MAX_COMMAND_DISPLAY_CHARS=160`(刀22·新叶子 `toolHeaderCap`)**·**常驻任务面板已完成(✓)行 strikethrough 对齐 CC `TaskListV2.strikethrough={isCompleted}`(刀23·新叶子 `taskLineStyle`)**·**TUI 斜杠菜单过滤/排序走 `rankSlashCommands` 子串/分段/描述匹配(刀24·新叶子 `slashMenuFilter`·与经典 REPL 收敛)**·**经典 ANSI 文件编辑 diff `renderStructuredDiff` 走真分 hunk 消费 `computeStructuredDiffHunks` SSOT、块间 `⋯ N unchanged lines` 分隔、`└` 摘要喂真 ±计数(刀25·与默认 TUI 收敛·非措辞/计数格式/折叠)**·**工具失败串受众拆分:给「人」的入参校验失败串折单行 `Invalid tool parameters`、完整分组只给「模型」+Ctrl+O 展开(刀26·新叶子 `ccUserFacingToolError` 消费产串方自识别签名 `ccValidationError.isValidationErrorMessage`·render-only 模型侧不动·与刀「给模型分组措辞」互补正交)**·**Read 头行「读取范围」后缀:路径后追加 `offset`/`limit`→`第 X-Y 行` / `从第 X 行起`,收敛 `toolDisplay.js`+`ToolLines.js` 两处头渲染(刀27·新叶子 `readRangeSuffix` 移植 CC `startLine + limit - 1` 算术·绝不移植 `pages` 分支因 Khy read 无该参数·offset/limit 无条件展示因 Khy 无 verbose 概念)**·**diff/预览溢出折叠标记文本+复数守卫收敛 `previewOverflowMarker`,两引擎(TUI `ToolLines`+经典 REPL `toolOutputRender`)共用,N=1 守单数 `+1 line`/`1 more row`(刀31·补刀27 扫描只核 gap 分隔行、漏看同函数 overflow marker 的盲点·门控开 N>1 字节一致·`toolDisplay.js:961` 的 `+N more tool uses` 刻意留作后续不顺手扩进)**·**常驻任务面板头行按状态计数:静态 `任务清单` 升级为 `任务清单(共 N 项:X 完成、Y 进行中、Z 待办[、W 错误])`,对齐 CC `TaskListV2` isStandalone 头行 `{N} tasks ({done} done, [{ip} in progress, ]{open} open)`(刀28·新叶子 `taskPanelHeader` 消费 `taskPanelLines.countTaskLinesByStatus` 图标逆映射 SSOT·计数覆盖全量可见+隐藏·进行中/error 仅 >0·error 诚实单列因 khy 有显式 ✗ 状态·任一诚实闸门触发回退静态标题)**;honest-NA=有序列表深度标号(无 AST 不可忠实)·标题 cyan+图标(khy 刻意主题)·链接 OSC-8(khy 刻意用显式 `text (dim url)`·不依赖 OSC-8 能力)·diff 整宽色条 TUI 路径(ink@6.8.0 output.js trimEnd 裁尾随背景空格·CC 用 @anthropic/ink 分叉无此 trim·改 ink=架构变更)·`~~text~~` 删除线(khy 已用 `~~(?!~)` 双波浪线·`~100` 单波浪线「约等于」本就不误删=已正确·CC 整体禁 del 是杀单 `~` 的连带·照搬反而**删掉正确特性**=NA)·agent tree glyph `├─`/`└─`(纯字形美学·logic 极薄)·spinner verbose/teammates 旁路(khy 此路径无此概念·诚实只 30s 钟生效不伪造旁路)·diff 展开态长词级配对行的彩色段重排(ink 自动换行多段 `<Text>`·本叶子只产宽度数值)·diff 词级 changeRatio 阈值常量/分词规则/LCS(刀16 只改度量口径不动这些)·**TUI 权限框选项过滤/`@`-mention 文件补全/thinking 块折叠**(刀24 核实=NA:权限框是固定 allow/deny 枚举无搜索·`@`-mention `computeFile` 已 `startsWith` 路径前缀与 CC 文件补全口径一致无发散·Khy 无独立 thinking 块渲染面造则 net-new)·**工具结果计数单复数 `=== 1 ? 'line':'lines'` / Grep `countLabel`**(刀27 扫候选核实=NA:Khy 人面工具结果走**透明输出**·不渲染 CC「Read N lines」计数摘要行·真存在的计数面要么是模型面 metadata 单复数无关,要么已正确守卫——**但刀31 补正**:该 NA 当时漏看 `ToolLines.js`/`toolOutputRender.js` 的 overflow marker `±N lines`/`N more rows` 未守复数,已由刀31 收口)·**Grep/Glob 结果摘要 `Found N matches across M files`**(刀27 核实=NA:Khy `toolResultSummary.js` 已渲染中文「找到 N 个匹配,跨 M 个文件」·中文无单复数)·**Read 头行 `pages` 分支**(刀27 核实=NA 且**诚实红线**:Khy `readFile.js` inputSchema **无** `pages` 参数·凭空造 `· pages N` = 臆造不携带的数据)。·diff 整宽色条 TUI 路径(ink@6.8.0 output.js trimEnd 裁尾随背景空格·CC 用 @anthropic/ink 分叉无此 trim·改 ink=架构变更)·`~~text~~` 删除线(khy 已用 `~~(?!~)` 双波浪线·`~100` 单波浪线「约等于」本就不误删=已正确·CC 整体禁 del 是杀单 `~` 的连带·照搬反而**删掉正确特性**=NA)·agent tree glyph `├─`/`└─`(纯字形美学·logic 极薄)·spinner verbose/teammates 旁路(khy 此路径无此概念·诚实只 30s 钟生效不伪造旁路)·diff 展开态长词级配对行的彩色段重排(ink 自动换行多段 `<Text>`·本叶子只产宽度数值)·diff 词级 changeRatio 阈值常量/分词规则/LCS(刀16 只改度量口径不动这些)·**TUI 权限框选项过滤/`@`-mention 文件补全/thinking 块折叠**(刀24 核实=NA:权限框是固定 allow/deny 枚举无搜索·`@`-mention `computeFile` 已 `startsWith` 路径前缀与 CC 文件补全口径一致无发散·Khy 无独立 thinking 块渲染面造则 net-new)·**bash 命令头 2 行+160 字两段截断(刀17 核实=NA**:khy `_truncateDisplayWidth` 刻意 `replace(/\s+/g,' ')` 把多行命令折成单行 space-join 头·另在 8 行 box 预览显全貌·CC 的 2 行规则是其**多行 Text 头**的不同但各自正确的选择·照搬不适配 khy 单行 `(...)` 头且删不掉任何坏东西=khy 刻意正确不改)·**literal stdout 折叠 head-only**(CC head-only vs khy `foldOutput` head+tail·各自正确·照搬会删 khy 正确尾部展示=NA)·**错误详情折叠 head-only**(刀18 核实=CC 错误折叠**本就是 head-only**·与 literal 的 head+tail 不同但**错误场景 head-only 即 CC 口径**·刀18 照 CC 取 head 10·诚实)·**任务清单优先级截断排序/strikethrough/bold/blocked-by 行内样式**(刀19 核实:khy 尾锚定截断与 CC recentCompleted→inProgress→pending→olderCompleted 优先级截断**各自正确**·strikethrough completed 需拆分图标/正文 Text 节点风险大·本刀只治隐藏项摘要分组口径不动行内样式=暂缓/NA→**刀23 已解**:改用**整行单 Text 划线**(不拆节点)绕开该风险,门控开 ✓ 行 `strikethrough:true`,整行含图标被划=刻意简化非缺陷)·**agent `elapsed` 改 number 类型**(刀20 核实=NA:agent-array 形状全部消费者 `formatStats` 字符串分支/`toolDisplay:639`/`panels:661` 都按 string verbatim push·`panels` docstring 明示 `@param {string}`·改 number 会断这些·`agentRenderer:140` `Math.max(...map(a=>a.elapsed||0))` 对字符串本就 NaN=**预存**非 TTY summary 独立 bug·本刀保 string 类型不引入也不修该预存 bug)·**HUD `{hideTrailingZeros}` 口径**(刀21 核实=NA:HUD 刻意省整分尾零 `60s→"1m"`·是与 spinner PLAIN 链**并存的另一约定**·非缺口·照搬会改 HUD 正确行为=不动)·**工具头命令 2 行多行头 `MAX_COMMAND_DISPLAY_LINES=2`**(刀22 核实=NA:Khy `summarizeArgs` 刻意 `replace(/\s+/g,' ')` 单行折叠·刀17 已裁定·只对齐字符上限 160 不引入多行头)·**grep pattern 头降到 CC 的 50**(刀22 核实=NA:Khy 60>CC 50·显得更多非更差·降级=删正确行为)·**经典 REPL `toolDisplay.js` 命令头**(刀22 核实=NA:刻意富设计=命令模式识别「git log …」+8 行 box 显全貌·与 TUI 单行头各自正确·刀17 已 NA·本刀仅治 TUI 这一支非两 call-site 收敛)·**markdown 表格表头按列对齐**(刀25 候选 B 核实=NA:`markdownRenderer.js:955` 表头恒左对齐是带注释的**刻意选择**「Header row is always left-aligned for readability」·Khy box-drawn 表格本就比 CC plain `| a | b |` 富·照搬 CC 表头右对齐=删既有刻意设计违诚实红线)·**经典 `renderDiff`(unified-diff)行号 gutter**(刀25 候选 C 核实=NA:与 diff-gutter 家族近重复·同属 `KHY_FULL_TUI=0` 经典回退面·价值低不追)·**给「人」折叠工具校验失败串的经典 readline REPL 路径**(刀26 核实=NA:`repl.js:7636` 那支 isErr 已 `.slice(0,120)` 把多行折成单行截断=不同的既有行为·且其分支无 `_expandableOutputs` 注册→若折成 `Invalid tool parameters` 将无 Ctrl+O 恢复=丢信息不诚实·故只治默认 TUI 这一支·经典 REPL honest-NA)·**CC `Error:` 前缀归一**(刀26 核实=NA:改动面遍及所有工具错误·Khy 错误本地化措辞各有设计=各自正确·非清晰缺口·照搬属措辞层变更不做)·**consecutive read/search 计数折叠**(CC 跨消息分类计数行·更大架构特性且渲染措辞英文语言相关·非单叶子刀·暂缓)·**任务面板头行的 standalone vs 非-standalone 渲染分叉**(刀28 核实=NA:CC 仅 `isStandalone` 显头行计数·非-standalone(嵌在别处)不显·Khy `TaskListPanel` 本就是常驻独立面板=恒等价 standalone·无第二渲染上下文可分叉·照搬 `isStandalone` 条件=net-new 无意义分支)·**CC `inProgressCount = length - completed - pending` 的「其余皆 in-progress」归并**(刀28 核实=刻意**不**照搬:CC 只有 completed/pending/in_progress 三态故把其余并进 in-progress 是其状态空间所限,Khy 有显式 error(✗)状态,把 error 并进「进行中」会**错报**=违诚实红线,故 Khy 按真实图标分别计数并单列 error)。·标题 cyan+图标(khy 刻意主题)·链接 OSC-8(khy 刻意用显式 `text (dim url)`·不依赖 OSC-8 能力)·diff 整宽色条 TUI 路径(ink@6.8.0 output.js trimEnd 裁尾随背景空格·CC 用 @anthropic/ink 分叉无此 trim·改 ink=架构变更)·`~~text~~` 删除线(khy 已用 `~~(?!~)` 双波浪线·`~100` 单波浪线「约等于」本就不误删=已正确·CC 整体禁 del 是杀单 `~` 的连带·照搬反而**删掉正确特性**=NA)·agent tree glyph `├─`/`└─`(纯字形美学·logic 极薄)·spinner verbose/teammates 旁路(khy 此路径无此概念·诚实只 30s 钟生效不伪造旁路)·diff 展开态长词级配对行的彩色段重排(ink 自动换行多段 `<Text>`·本叶子只产宽度数值)·diff 词级 changeRatio 阈值常量/分词规则/LCS(刀16 只改度量口径不动这些)·**TUI 权限框选项过滤/`@`-mention 文件补全/thinking 块折叠**(刀24 核实=NA:权限框是固定 allow/deny 枚举无搜索·`@`-mention `computeFile` 已 `startsWith` 路径前缀与 CC 文件补全口径一致无发散·Khy 无独立 thinking 块渲染面造则 net-new)·**bash 命令头 2 行+160 字两段截断(刀17 核实=NA**:khy `_truncateDisplayWidth` 刻意 `replace(/\s+/g,' ')` 把多行命令折成单行 space-join 头·另在 8 行 box 预览显全貌·CC 的 2 行规则是其**多行 Text 头**的不同但各自正确的选择·照搬不适配 khy 单行 `(...)` 头且删不掉任何坏东西=khy 刻意正确不改)·**literal stdout 折叠 head-only**(CC head-only vs khy `foldOutput` head+tail·各自正确·照搬会删 khy 正确尾部展示=NA)·**错误详情折叠 head-only**(刀18 核实=CC 错误折叠**本就是 head-only**·与 literal 的 head+tail 不同但**错误场景 head-only 即 CC 口径**·刀18 照 CC 取 head 10·诚实)·**任务清单优先级截断排序/strikethrough/bold/blocked-by 行内样式**(刀19 核实:khy 尾锚定截断与 CC recentCompleted→inProgress→pending→olderCompleted 优先级截断**各自正确**·strikethrough completed 需拆分图标/正文 Text 节点风险大·本刀只治隐藏项摘要分组口径不动行内样式=暂缓/NA→**刀23 已解**:改用**整行单 Text 划线**(不拆节点)绕开该风险,门控开 ✓ 行 `strikethrough:true`,整行含图标被划=刻意简化非缺陷)·**agent `elapsed` 改 number 类型**(刀20 核实=NA:agent-array 形状全部消费者 `formatStats` 字符串分支/`toolDisplay:639`/`panels:661` 都按 string verbatim push·`panels` docstring 明示 `@param {string}`·改 number 会断这些·`agentRenderer:140` `Math.max(...map(a=>a.elapsed||0))` 对字符串本就 NaN=**预存**非 TTY summary 独立 bug·本刀保 string 类型不引入也不修该预存 bug)·**HUD `{hideTrailingZeros}` 口径**(刀21 核实=NA:HUD 刻意省整分尾零 `60s→"1m"`·是与 spinner PLAIN 链**并存的另一约定**·非缺口·照搬会改 HUD 正确行为=不动)·**工具头命令 2 行多行头 `MAX_COMMAND_DISPLAY_LINES=2`**(刀22 核实=NA:Khy `summarizeArgs` 刻意 `replace(/\s+/g,' ')` 单行折叠·刀17 已裁定·只对齐字符上限 160 不引入多行头)·**grep pattern 头降到 CC 的 50**(刀22 核实=NA:Khy 60>CC 50·显得更多非更差·降级=删正确行为)·**经典 REPL `toolDisplay.js` 命令头**(刀22 核实=NA:刻意富设计=命令模式识别「git log …」+8 行 box 显全貌·与 TUI 单行头各自正确·刀17 已 NA·本刀仅治 TUI 这一支非两 call-site 收敛)·**markdown 表格表头按列对齐**(刀25 候选 B 核实=NA:`markdownRenderer.js:955` 表头恒左对齐是带注释的**刻意选择**「Header row is always left-aligned for readability」·Khy box-drawn 表格本就比 CC plain `| a | b |` 富·照搬 CC 表头右对齐=删既有刻意设计违诚实红线)·**经典 `renderDiff`(unified-diff)行号 gutter**(刀25 候选 C 核实=NA:与 diff-gutter 家族近重复·同属 `KHY_FULL_TUI=0` 经典回退面·价值低不追)·**给「人」折叠工具校验失败串的经典 readline REPL 路径**(刀26 核实=NA:`repl.js:7636` 那支 isErr 已 `.slice(0,120)` 把多行折成单行截断=不同的既有行为·且其分支无 `_expandableOutputs` 注册→若折成 `Invalid tool parameters` 将无 Ctrl+O 恢复=丢信息不诚实·故只治默认 TUI 这一支·经典 REPL honest-NA)·**CC `Error:` 前缀归一**(刀26 核实=NA:改动面遍及所有工具错误·Khy 错误本地化措辞各有设计=各自正确·非清晰缺口·照搬属措辞层变更不做)·**consecutive read/search 计数折叠**(CC 跨消息分类计数行·更大架构特性且渲染措辞英文语言相关·非单叶子刀·暂缓)·**工具结果计数单复数 `=== 1 ? 'line':'lines'` / `'page':'pages'` / Grep `countLabel`**(刀27 扫候选核实=NA:Khy 人面工具结果走**透明输出**——直显真实行/结构化 diff,**不渲染** CC 那种「Read N lines」计数摘要行;真存在的计数面要么是**模型面** metadata `${n} total lines`(给模型·CC 亦同·单复数无关),要么已正确守卫单复数 `line${n!==1?'s':''}`(`diffRenderer.js:388`/`ToolLines.js:209`)·无可达显示发散,造计数面=臆造 Khy 刻意不携带的摘要面)·**Grep/Glob 结果摘要 `Found N matches across M files`**(刀27 扫候选核实=NA:Khy `toolResultSummary.js:148-174` 已 mode-aware 且渲染中文「找到 N 个匹配,跨 M 个文件」·中文无单复数·CC 的 `countLabel.slice(0,-1)` 单数化对中文不适用=各自正确本地化)·**File-write `Wrote N lines`/`+N lines` overflow 单复数**(刀27 扫候选核实=NA:Khy `toolResultSummary.js:361` 中文「已写入 …(N 行)」·中文无单复数)·**扩展思考块折叠/timestamp/cost byline/relative-time/file-size/number 格式**(刀27 扫候选核实=NA:`thinkingDuration.js`/`ccFormat.js`(`ccFormatFileSize`/`ccFormatNumber`/`ccFormatTokens`/`ccFormatDuration`/`ccRelativeAgeParts`/`ccBriefTimestamp`/`ccFormatCost`)已忠实移植 CC `format.ts` 全族·CC Message.tsx 本就无 cost byline·均已覆盖)·**Read 头行 `pages` 分支**(刀27 核实=NA 且**诚实红线**:Khy `readFile.js` inputSchema **无** `pages` 参数·凭空造 `· pages N` = 臆造不携带的数据·刀27 只移植 offset/limit 行区间算术绝不碰 pages)。
  - **本轮 honest-NA 核实归档**(防后续重复追):② **plural `n===1` vs khy `>1`**——核实 4 个 `>1?'s':''` call-site(`FileEditTool:272/282`、`MultiEditTool:243`、`editFile:152`)**全部**位于 `count===0→error` 守卫下游(`editFile:117`、`FileEditTool` 同模式、`MultiEditTool:143` 非空数组守卫),count 恒 `>=1`→`>1` 与 `!==1` **逐字节同输出**(仅 `0` 处发散而 `0` 永不到达该串)=**非真 bug**,不改(守诚实红线绝不造「修复」)。③ **timeFormat `formatTimeAgo`/`formatRelativeTimestamp` 的 `Math.round` 向上虚报**(`90s→"2m"`)——核实**零消费者**(仅 `formatDurationPrecise` 被 `diagnosticEvents` 用),属**死导出**无任何显示面→对齐它不改变任何用户可见显示=NA。④ **`formatResetTime` 速率限制重置展示**——khy 速率限制全为内部 cooldown/backoff(`apiKeyPool`/`circuitBreaker`),**无用户可见「resets at」展示面**→对齐=net-new 特性非对齐=NA。

承 [[project_cc_alignment_missing_commands_tools]] · 接线契约见 `[DESIGN-ARCH-060]`。
